mirror of
https://github.com/LCTT/TranslateProject.git
synced 2026-09-04 04:32:49 +08:00
清除过期文章
This commit is contained in:
@@ -1,133 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Research log: gene signatures and connectivity map)
|
||||
[#]: via: (https://www.jtolio.com/2015/11/research-log-gene-signatures-and-connectivity-map)
|
||||
[#]: author: (jtolio.com https://www.jtolio.com/)
|
||||
|
||||
Research log: gene signatures and connectivity map
|
||||
======
|
||||
|
||||
Happy Thanksgiving everyone!
|
||||
|
||||
### Context
|
||||
|
||||
This is the third post in my continuing series on my attempts at research. Previously we talked about:
|
||||
|
||||
* [what I’m doing, cell states, and microarrays][1]
|
||||
* and then [more about microarrays and R][2].
|
||||
|
||||
|
||||
|
||||
By the end of last week we had discussed how to get a table of normalized gene expression intensities that looks like this:
|
||||
|
||||
```
|
||||
ENSG00000280099_at 0.15484421
|
||||
ENSG00000280109_at 0.16881395
|
||||
ENSG00000280178_at -0.19621641
|
||||
ENSG00000280316_at 0.08622216
|
||||
ENSG00000280401_at 0.15966256
|
||||
ENSG00000281205_at -0.02085352
|
||||
...
|
||||
```
|
||||
|
||||
The reason for doing this is to figure out which genes are related, and perhaps more importantly, what a cell is even doing.
|
||||
|
||||
_Summary:_ new post, also, I’m bringing back the short section summaries.
|
||||
|
||||
### Cell lines
|
||||
|
||||
The first thing to do when trying to figure out what cells are doing is to choose a cell. There’s all sorts of cells. Healthy brain cells, cancerous blood cells, bruised skin cells, etc.
|
||||
|
||||
For any experiment, you’ll need a control to eliminate noise and apply statistical tests for validity. If you don’t use a control, the effect you’re seeing may not even exist, and so for any experiment with cells, you will need a control cell.
|
||||
|
||||
Cells often divide, which means that a cell, once chosen, will duplicate itself for you in the presence of the appropriate resources. Not all cells divide ad nauseam which provides some challenges, but many cells under study luckily do.
|
||||
|
||||
So, a _cell line_ is simply a set of cells that have all replicated from a specific chosen initial cell. Any set of cells from a cell line will be as identical as possible (unless you screwed up! geez). They will be the same type of cell with the same traits and behaviors, at least, as much as possible.
|
||||
|
||||
_Summary:_ a cell line is a large amount of cells that are as close to being the same as possible.
|
||||
|
||||
### Perturbagens
|
||||
|
||||
There are many things that might affect what a cell is doing. Drugs, agitation, temperature, disease, cancer, gene splicing, small molecules (maybe you give a cell more iron or calcium or something), hormones, light, Jello, ennui, etc. Given any particular cell line, giving a cell from that cell line one of these _perturbagens_, or, perturbing the cell in a specific way, when compared to a control will say what that cell does differently in the face of that perturbagen.
|
||||
|
||||
If you’d like to find out what exactly a certain type of cell does when you give it lemon lime soda, then you choose the right cell line, leave out some control cells and give the rest of the cells soda.
|
||||
|
||||
Then, you measure gene expression intensities for both the control cells and the perturbed cells. The _differential expression_ of genes between the perturbed cells and the controls cells is likely due to the introduction of the lemon lime soda.
|
||||
|
||||
Genes that end up getting expressed _more_ in the presence of the soda are considered _up-regulated_, whereas genes that end up getting expressed _less_ are considered _down-regulated_. The degree to which a gene is up or down regulated constitutes how much of an effect the soda may have had on that gene.
|
||||
|
||||
Of course, all of this has such a significant amount of experimental noise that you could find pretty much anything. You’ll need to replicate your experiment independently a few times before you publish that lemon lime soda causes increased expression in the [Sonic hedgehog gene][3].
|
||||
|
||||
_Summary:_ A perturbagen is something you introduce/do to a cell to change its behavior, such as drugs or throwing it at a wall or something. The wall perturbagen.
|
||||
|
||||
### Gene signature
|
||||
|
||||
For a given change or perturbagen to a cell, we now have enough to compute lists of up-regulated and down-regulated genes and the magnitude change in expression for each gene.
|
||||
|
||||
This gene expression pattern for some subset of important genes (perhaps the most changed in expression) is called a _gene signature_, and gene signatures are very useful. By comparing signatures, you can:
|
||||
|
||||
* identify or compare cell states
|
||||
* find sets of positively or negatively correlated genes
|
||||
* find similar disease signatures
|
||||
* find similar drug signatures
|
||||
* find drug signatures that might counteract opposite disease signatures.
|
||||
|
||||
|
||||
|
||||
(That last bullet point is essentially where I’m headed with my research.)
|
||||
|
||||
_Summary:_ a gene signature is a short summary of the most important gene expression differences a perturbagen causes in a cell.
|
||||
|
||||
### Drugs!
|
||||
|
||||
The pharmaceutical industry is constantly on the lookout for new breakthrough drugs that might represent huge windfalls in cash, and drugs don’t always work as planned. Many drugs spend years in research and development, only to ultimately find poor efficacy or adoption. Sometimes drugs even become known [much more for their side-effects than their originally intended therapy][4].
|
||||
|
||||
The practical upshot is that there’s countless FDA-approved drugs that represent decades of work that are simply underused or even unused entirely. These drugs have already cleared many challenging regulatory hurdles, but are simply and quite literally cures looking for a disease.
|
||||
|
||||
If even just one of these drugs can be given a new lease on life for some yet-to-be-cured disease, then perhaps we can give some people new leases on life!
|
||||
|
||||
_Summary:_ instead of developing new drugs, there’s already lots of drugs that aren’t being used. Maybe we can find matching diseases!
|
||||
|
||||
### The Connectivity Map project
|
||||
|
||||
The [Broad Institute’s Connectivity Map project][5] isn’t particularly new anymore, but it represents a ground breaking and promising idea - we can dump a bunch of signatures into a database and construct all sorts of new hypotheses we might not even have thought to check before.
|
||||
|
||||
To prove out the usefulness of this idea, the Connectivity Map (or cmap) project chose 5 different cell lines (all cancer cells, which are easy to get to replicate!) and a library of FDA approved drugs, and then gave some cells these drugs.
|
||||
|
||||
They then constructed a database of all of the signatures they computed for each possible perturbagen they measured. Finally, they constructed a web interface where a user can upload a gene signature and get a result list back of all of the signatures they collected, ordered by the most to least similar. You can totally go sign up and [try it out][5].
|
||||
|
||||
This simple tool is surprisingly powerful. It allows you to find similar drugs to a drug you know, but it also allows you to find drugs that might counteract a disease you’ve created a signature for.
|
||||
|
||||
Ultimately, the project led to [a number of successful applications][6]. So useful was it that the Broad Institute has doubled down and created the much larger and more comprehensive [LINCS Project][7] that targets an order of magnitude more cell lines (77) and more perturbagens (42,532, compared to cmap’s 6100). You can sign up and use that one too!
|
||||
|
||||
_Summary_: building a system that supports querying signature connections has already proved to be super useful.
|
||||
|
||||
### Whew
|
||||
|
||||
Alright, I wrote most of this on a plane yesterday but since I should now be spending time with family I’m going to cut it short here.
|
||||
|
||||
Stay tuned for next week!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.jtolio.com/2015/11/research-log-gene-signatures-and-connectivity-map
|
||||
|
||||
作者:[jtolio.com][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.jtolio.com/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.jtolio.com/writing/2015/11/research-log-cell-states-and-microarrays/
|
||||
[2]: https://www.jtolio.com/writing/2015/11/research-log-r-and-more-microarrays/
|
||||
[3]: https://en.wikipedia.org/wiki/Sonic_hedgehog
|
||||
[4]: https://en.wikipedia.org/wiki/Sildenafil#History
|
||||
[5]: https://www.broadinstitute.org/cmap/
|
||||
[6]: https://www.broadinstitute.org/cmap/publications.jsp
|
||||
[7]: http://www.lincscloud.org/
|
||||
@@ -1,658 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Writing Advanced Web Applications with Go)
|
||||
[#]: via: (https://www.jtolio.com/2017/01/writing-advanced-web-applications-with-go)
|
||||
[#]: author: (jtolio.com https://www.jtolio.com/)
|
||||
|
||||
Writing Advanced Web Applications with Go
|
||||
======
|
||||
|
||||
Web development in many programming environments often requires subscribing to some full framework ethos. With [Ruby][1], it’s usually [Rails][2] but could be [Sinatra][3] or something else. With [Python][4], it’s often [Django][5] or [Flask][6]. With [Go][7], it’s…
|
||||
|
||||
If you spend some time in Go communities like the [Go mailing list][8] or the [Go subreddit][9], you’ll find Go newcomers frequently wondering what web framework is best to use. [There][10] [are][11] [quite][12] [a][13] [few][14] [Go][15] [frameworks][16] ([and][17] [then][18] [some][19]), so which one is best seems like a reasonable question. Without fail, though, the strong recommendation of the Go community is to [avoid web frameworks entirely][20] and just stick with the standard library as long as possible. Here’s [an example from the Go mailing list][21] and here’s [one from the subreddit][22].
|
||||
|
||||
It’s not bad advice! The Go standard library is very rich and flexible, much more so than many other languages, and designing a web application in Go with just the standard library is definitely a good choice.
|
||||
|
||||
Even when these Go frameworks call themselves minimalistic, they can’t seem to help themselves avoid using a different request handler interface than the default standard library [http.Handler][23], and I think this is the biggest source of angst about why frameworks should be avoided. If everyone standardizes on [http.Handler][23], then dang, all sorts of things would be interoperable!
|
||||
|
||||
Before Go 1.7, it made some sense to give in and use a different interface for handling HTTP requests. But now that [http.Request][24] has the [Context][25] and [WithContext][26] methods, there truly isn’t a good reason any longer.
|
||||
|
||||
I’ve done a fair share of web development in Go and I’m here to share with you both some standard library development patterns I’ve learned and some code I’ve found myself frequently needing. The code I’m sharing is not for use instead of the standard library, but to augment it.
|
||||
|
||||
Overall, if this blog post feels like it’s predominantly plugging various little standalone libraries from my [Webhelp non-framework][27], that’s because it is. It’s okay, they’re little standalone libraries. Only use the ones you want!
|
||||
|
||||
If you’re new to Go web development, I suggest reading the Go documentation’s [Writing Web Applications][28] article first.
|
||||
|
||||
### Middleware
|
||||
|
||||
A frequent design pattern for server-side web development is the concept of _middleware_, where some portion of the request handler wraps some other portion of the request handler and does some preprocessing or routing or something. This is a big component of how [Express][29] is organized on [Node][30], and how Express middleware and [Negroni][17] middleware works is almost line-for-line identical in design.
|
||||
|
||||
Good use cases for middleware are things such as:
|
||||
|
||||
* making sure a user is logged in, redirecting if not,
|
||||
* making sure the request came over HTTPS,
|
||||
* making sure a session is set up and loaded from a session database,
|
||||
* making sure we logged information before and after the request was handled,
|
||||
* making sure the request was routed to the right handler,
|
||||
* and so on.
|
||||
|
||||
|
||||
|
||||
Composing your web app as essentially a chain of middleware handlers is a very powerful and flexible approach. It allows you to avoid a lot of [cross-cutting concerns][31] and have your code factored in very elegant and easy-to-maintain ways. By wrapping a set of handlers with middleware that ensures a user is logged in prior to actually attempting to handle the request, the individual handlers no longer need mistake-prone copy-and-pasted code to ensure the same thing.
|
||||
|
||||
So, middleware is good. However, if Negroni or other frameworks are any indication, you’d think the standard library’s `http.Handler` isn’t up to the challenge. Negroni adds its own `negroni.Handler` just for the sake of making middleware easier. There’s no reason for this.
|
||||
|
||||
Here is a full middleware implementation for ensuring a user is logged in, assuming a `GetUser(*http.Request)` function but otherwise just using the standard library:
|
||||
|
||||
```
|
||||
func RequireUser(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
user, err := GetUser(req)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if user == nil {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
h.ServeHTTP(w, req)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Here’s how it’s used (just wrap another handler!):
|
||||
|
||||
```
|
||||
func main() {
|
||||
http.ListenAndServe(":8080", RequireUser(http.HandlerFunc(myHandler)))
|
||||
}
|
||||
```
|
||||
|
||||
Express, Negroni, and other frameworks expect this kind of signature for a middleware-supporting handler:
|
||||
|
||||
```
|
||||
type Handler interface {
|
||||
// don't do this!
|
||||
ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.HandlerFunc)
|
||||
}
|
||||
```
|
||||
|
||||
There’s really no reason for adding the `next` argument - it reduces cross-library compatibility. So I say, don’t use `negroni.Handler` (or similar). Just use `http.Handler`!
|
||||
|
||||
### Composability
|
||||
|
||||
Hopefully I’ve sold you on middleware as a good design philosophy.
|
||||
|
||||
Probably the most commonly-used type of middleware is request routing, or muxing (seems like we should call this demuxing but what do I know). Some frameworks are almost solely focused on request routing. [gorilla/mux][32] seems more popular than any other part of the [Gorilla][33] library. I think the reason for this is that even though the Go standard library is completely full featured and has a good [ServeMux][34] implementation, it doesn’t make the right thing the default.
|
||||
|
||||
So! Let’s talk about request routing and consider the following problem. You, web developer extraordinaire, want to serve some HTML from your web server at `/hello/` but also want to serve some static assets from `/static/`. Let’s take a quick stab.
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func hello(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write([]byte("hello, world!"))
|
||||
}
|
||||
|
||||
func main() {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/hello/", http.HandlerFunc(hello))
|
||||
mux.Handle("/static/", http.FileServer(http.Dir("./static-assets")))
|
||||
http.ListenAndServe(":8080", mux)
|
||||
}
|
||||
```
|
||||
|
||||
If you visit `http://localhost:8080/hello/`, you’ll be rewarded with a friendly “hello, world!” message.
|
||||
|
||||
If you visit `http://localhost:8080/static/` on the other hand (assuming you have a folder of static assets in `./static-assets`), you’ll be surprised and frustrated. This code tries to find the source content for the request `/static/my-file` at `./static-assets/static/my-file`! There’s an extra `/static` in there!
|
||||
|
||||
Okay, so this is why `http.StripPrefix` exists. Let’s fix it.
|
||||
|
||||
```
|
||||
mux.Handle("/static/", http.StripPrefix("/static",
|
||||
http.FileServer(http.Dir("./static-assets"))))
|
||||
```
|
||||
|
||||
`mux.Handle` combined with `http.StripPrefix` is such a common pattern that I think it should be the default. Whenever a request router processes a certain amount of URL elements, it should strip them off the request so the wrapped `http.Handler` doesn’t need to know its absolute URL and only needs to be concerned with its relative one.
|
||||
|
||||
In [Russ Cox][35]’s recent [TiddlyWeb backend][36], I would argue that every time `strings.TrimPrefix` is needed to remove the full URL from the handler’s incoming path arguments, it is an unnecessary cross-cutting concern, unfortunately imposed by `http.ServeMux`. (An example is [line 201 in tiddly.go][37].)
|
||||
|
||||
I’d much rather have the default `mux` behavior work more like a directory of registered elements that by default strips off the ancestor directory before handing the request to the next middleware handler. It’s much more composable. To this end, I’ve written a simple muxer that works in this fashion called [whmux.Dir][38]. It is essentially `http.ServeMux` and `http.StripPrefix` combined. Here’s the previous example reworked to use it:
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"gopkg.in/webhelp.v1/whmux"
|
||||
)
|
||||
|
||||
func hello(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write([]byte("hello, world!"))
|
||||
}
|
||||
|
||||
func main() {
|
||||
mux := whmux.Dir{
|
||||
"hello": http.HandlerFunc(hello),
|
||||
"static": http.FileServer(http.Dir("./static-assets")),
|
||||
}
|
||||
http.ListenAndServe(":8080", mux)
|
||||
}
|
||||
```
|
||||
|
||||
There are other useful mux implementations inside the [whmux][39] package that demultiplex on various aspects of the request path, request method, request host, or pull arguments out of the request and place them into the context, such as a [whmux.IntArg][40] or [whmux.StringArg][41]. This brings us to [contexts][42].
|
||||
|
||||
### Contexts
|
||||
|
||||
Request contexts are a recent addition to the Go 1.7 standard library, but the idea of [contexts has been around since mid-2014][43]. As of Go 1.7, they were added to the standard library ([“context”][42]), but are available for older Go releases in the original location ([“golang.org/x/net/context”][44]).
|
||||
|
||||
First, here’s the definition of the `context.Context` type that `(*http.Request).Context()` returns:
|
||||
|
||||
```
|
||||
type Context interface {
|
||||
Done() <-chan struct{}
|
||||
Err() error
|
||||
Deadline() (deadline time.Time, ok bool)
|
||||
|
||||
Value(key interface{}) interface{}
|
||||
}
|
||||
```
|
||||
|
||||
Talking about `Done()`, `Err()`, and `Deadline()` are enough for an entirely different blog post, so I’m going to ignore them at least for now and focus on `Value(interface{})`.
|
||||
|
||||
As a motivating problem, let’s say that the `GetUser(*http.Request)` method we assumed earlier is expensive, and we only want to call it once per request. We certainly don’t want to call it once to check that a user is logged in, and then again when we actually need the `*User` value. With `(*http.Request).WithContext` and `context.WithValue`, we can pass the `*User` down to the next middleware precomputed!
|
||||
|
||||
Here’s the new middleware:
|
||||
|
||||
```
|
||||
type userKey int
|
||||
|
||||
func RequireUser(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
user, err := GetUser(req)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if user == nil {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, userKey(0), user)
|
||||
h.ServeHTTP(w, req.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Now, handlers that are protected by this `RequireUser` handler can load the previously computed `*User` value like this:
|
||||
|
||||
```
|
||||
if user, ok := req.Context().Value(userKey(0)).(*User); ok {
|
||||
// there's a valid user!
|
||||
}
|
||||
```
|
||||
|
||||
Contexts allow us to pass optional values to handlers down the chain in a way that is relatively type-safe and flexible. None of the above context logic requires anything outside of the standard library.
|
||||
|
||||
#### Aside about context keys
|
||||
|
||||
There was a curious piece of code in the above example. At the top, we defined a `type userKey int`, and then always used it as `userKey(0)`.
|
||||
|
||||
One of the possible problems with contexts is the `Value()` interface lends itself to a global namespace where you can stomp on other context users and use conflicting key names. Above, we used `type userKey` because it’s an unexported type in your package. It will never compare equal (without a cast) to any other type, including `int`, in Go. This gives us a way to namespace keys to your package, even though the `Value()` method is still a sort of global namespace.
|
||||
|
||||
Because the need for this is so common, the `webhelp` package defines a [GenSym()][45] helper that will create a brand new, never-before-seen, unique value for use as a context key.
|
||||
|
||||
If we used [GenSym()][45], then `type userKey int` would become `var userKey = webhelp.GenSym()` and `userKey(0)` would simply become `userKey`.
|
||||
|
||||
#### Back to whmux.StringArg
|
||||
|
||||
Armed with this new context behavior, we can now present a `whmux.StringArg` example:
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"gopkg.in/webhelp.v1/whmux"
|
||||
)
|
||||
|
||||
var (
|
||||
pageName = whmux.NewStringArg()
|
||||
)
|
||||
|
||||
func page(w http.ResponseWriter, req *http.Request) {
|
||||
name := pageName.Get(req.Context())
|
||||
|
||||
fmt.Fprintf(w, "Welcome to %s", name)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// pageName.Shift pulls the next /-delimited string out of the request's
|
||||
// URL.Path and puts it into the context instead.
|
||||
pageHandler := pageName.Shift(http.HandlerFunc(page))
|
||||
|
||||
http.ListenAndServe(":8080", whmux.Dir{
|
||||
"wiki": pageHandler,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Pre-Go-1.7 support
|
||||
|
||||
Contexts let you do some pretty cool things. But let’s say you’re stuck with something before Go 1.7 (for instance, App Engine is currently Go 1.6).
|
||||
|
||||
That’s okay! I’ve backported all of the neat new context features to Go 1.6 and earlier in a forwards compatible way!
|
||||
|
||||
With the [whcompat][46] package, `req.Context()` becomes `whcompat.Context(req)`, and `req.WithContext(ctx)` becomes `whcompat.WithContext(req, ctx)`. The `whcompat` versions work with all releases of Go. Yay!
|
||||
|
||||
There’s a bit of unpleasantness behind the scenes to make this happen. Specifically, for pre-1.7 builds, a global map indexed by `req.URL` is kept, and a finalizer is installed on `req` to clean up. So don’t change what `req.URL` points to and this will work fine. In practice it’s not a problem.
|
||||
|
||||
`whcompat` adds additional backwards-compatibility helpers. In Go 1.7 and on, the context’s `Done()` channel is closed (and `Err()` is set), whenever the request is done processing. If you want this behavior in Go 1.6 and earlier, just use the [whcompat.DoneNotify][47] middleware.
|
||||
|
||||
In Go 1.8 and on, the context’s `Done()` channel is closed when the client goes away, even if the request hasn’t completed. If you want this behavior in Go 1.7 and earlier, just use the [whcompat.CloseNotify][48] middleware, though beware that it costs an extra goroutine.
|
||||
|
||||
### Error handling
|
||||
|
||||
How you handle errors can be another cross-cutting concern, but with good application of context and middleware, it too can be beautifully cleaned up so that the responsibilities lie in the correct place.
|
||||
|
||||
Problem statement: your `RequireUser` middleware needs to handle an authentication error differently between your HTML endpoints and your JSON API endpoints. You want to use `RequireUser` for both types of endpoints, but with your HTML endpoints you want to return a user-friendly error page, and with your JSON API endpoints you want to return an appropriate JSON error state.
|
||||
|
||||
In my opinion, the right thing to do is to have contextual error handlers, and luckily, we have a context for contextual information!
|
||||
|
||||
First, we need an error handler interface.
|
||||
|
||||
```
|
||||
type ErrHandler interface {
|
||||
HandleError(w http.ResponseWriter, req *http.Request, err error)
|
||||
}
|
||||
```
|
||||
|
||||
Next, let’s make a middleware that registers the error handler in the context:
|
||||
|
||||
```
|
||||
var errHandler = webhelp.GenSym() // see the aside about context keys
|
||||
|
||||
func HandleErrWith(eh ErrHandler, h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
ctx := context.WithValue(whcompat.Context(req), errHandler, eh)
|
||||
h.ServeHTTP(w, whcompat.WithContext(req, ctx))
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Last, let’s make a function that will use the registered error handler for errors:
|
||||
|
||||
```
|
||||
func HandleErr(w http.ResponseWriter, req *http.Request, err error) {
|
||||
if handler, ok := whcompat.Context(req).Value(errHandler).(ErrHandler); ok {
|
||||
handler.HandleError(w, req, err)
|
||||
return
|
||||
}
|
||||
log.Printf("error: %v", err)
|
||||
http.Error(w, "internal server error", http.StatusInternalServerError)
|
||||
}
|
||||
```
|
||||
|
||||
Now, as long as everything uses `HandleErr` to handle errors, our JSON API can handle errors with JSON responses, and our HTML endpoints can handle errors with HTML responses.
|
||||
|
||||
Of course, the [wherr][49] package implements this all for you, and the [whjson][49] package even implements a friendly JSON API error handler.
|
||||
|
||||
Here’s how you might use it:
|
||||
|
||||
```
|
||||
var userKey = webhelp.GenSym()
|
||||
|
||||
func RequireUser(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
user, err := GetUser(req)
|
||||
if err != nil {
|
||||
wherr.Handle(w, req, wherr.InternalServerError.New("failed to get user"))
|
||||
return
|
||||
}
|
||||
if user == nil {
|
||||
wherr.Handle(w, req, wherr.Unauthorized.New("no user found"))
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, userKey, user)
|
||||
h.ServeHTTP(w, req.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func userpage(w http.ResponseWriter, req *http.Request) {
|
||||
user := req.Context().Value(userKey).(*User)
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
userpageTmpl.Execute(w, user)
|
||||
}
|
||||
|
||||
func username(w http.ResponseWriter, req *http.Request) {
|
||||
user := req.Context().Value(userKey).(*User)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{"user": user})
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.ListenAndServe(":8080", whmux.Dir{
|
||||
"api": wherr.HandleWith(whjson.ErrHandler,
|
||||
RequireUser(whmux.Dir{
|
||||
"username": http.HandlerFunc(username),
|
||||
})),
|
||||
"user": RequireUser(http.HandlerFunc(userpage)),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
#### Aside about the spacemonkeygo/errors package
|
||||
|
||||
The default [wherr.Handle][50] implementation understands all of the [error classes defined in the wherr top level package][51].
|
||||
|
||||
These error classes are implemented using the [spacemonkeygo/errors][52] library and the [spacemonkeygo/errors/errhttp][53] extensions. You don’t have to use this library or these errors, but the benefit is that your error instances can be extended to include HTTP status code messages and information, which once again, provides for a nice elimination of cross-cutting concerns in your error handling logic.
|
||||
|
||||
See the [spacemonkeygo/errors][52] package for more details.
|
||||
|
||||
_**Update 2018-04-19:** After a few years of use, my friend condensed some lessons we learned and the best parts of `spacemonkeygo/errors` into a new, more concise, better library, over at [github.com/zeebo/errs][54]. Consider using that instead!_
|
||||
|
||||
### Sessions
|
||||
|
||||
Go’s standard library has great support for cookies, but cookies by themselves aren’t usually what a developer thinks of when she thinks about sessions. Cookies are unencrypted, unauthenticated, and readable by the user, and perhaps you don’t want that with your session data.
|
||||
|
||||
Further, sessions can be stored in cookies, but could also be stored in a database to provide features like session revocation and querying. There’s lots of potential details about the implementation of sessions.
|
||||
|
||||
Request handlers, however, probably don’t care too much about the implementation details of the session. Request handlers usually just want a bucket of keys and values they can store safely and securely.
|
||||
|
||||
The [whsess][55] package implements middleware for registering an arbitrary session store (a default cookie-based session store is provided), and implements helpers for retrieving and saving new values into the session.
|
||||
|
||||
The default cookie-based session store implements encryption and authentication via the excellent [nacl/secretbox][56] package.
|
||||
|
||||
Usage is like this:
|
||||
|
||||
```
|
||||
func handler(w http.ResponseWriter, req *http.Request) {
|
||||
ctx := whcompat.Context(req)
|
||||
sess, err := whsess.Load(ctx, "namespace")
|
||||
if err != nil {
|
||||
wherr.Handle(w, req, err)
|
||||
return
|
||||
}
|
||||
if loggedIn, _ := sess.Values["logged_in"].(bool); loggedIn {
|
||||
views, _ := sess.Values["views"].(int64)
|
||||
sess.Values["views"] = views + 1
|
||||
sess.Save(w)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.ListenAndServe(":8080", whsess.HandlerWithStore(
|
||||
whsess.NewCookieStore(secret), http.HandlerFunc(handler)))
|
||||
}
|
||||
```
|
||||
|
||||
### Logging
|
||||
|
||||
The Go standard library by default doesn’t log incoming requests, outgoing responses, or even just what port the HTTP server is listening on.
|
||||
|
||||
The [whlog][57] package implements all three. The [whlog.LogRequests][58] middleware will log requests as they start. The [whlog.LogResponses][59] middleware will log requests as they end, along with status code and timing information. [whlog.ListenAndServe][60] will log the address the server ultimately listens on (if you specify “:0” as your address, a port will be randomly chosen, and [whlog.ListenAndServe][60] will log it).
|
||||
|
||||
[whlog.LogResponses][59] deserves special mention for how it does what it does. It uses the [whmon][61] package to instrument the outgoing `http.ResponseWriter` to keep track of response information.
|
||||
|
||||
Usage is like this:
|
||||
|
||||
```
|
||||
func main() {
|
||||
whlog.ListenAndServe(":8080", whlog.LogResponses(whlog.Default, handler))
|
||||
}
|
||||
```
|
||||
|
||||
#### App engine logging
|
||||
|
||||
App engine logging is unconventional crazytown. The standard library logger doesn’t work by default on App Engine, because App Engine logs _require_ the request context. This is unfortunate for libraries that don’t necessarily run on App Engine all the time, as their logging information doesn’t make it to the App Engine request-specific logger.
|
||||
|
||||
Unbelievably, this is fixable with [whgls][62], which uses my terrible, terrible (but recently improved) [Goroutine-local storage library][63] to store the request context on the current stack, register a new log output, and fix logging so standard library logging works with App Engine again.
|
||||
|
||||
### Template handling
|
||||
|
||||
Go’s standard library [html/template][64] package is excellent, but you’ll be unsurprised to find there’s a few tasks I do with it so commonly that I’ve written additional support code.
|
||||
|
||||
The [whtmpl][65] package really does two things. First, it provides a number of useful helper methods for use within templates, and second, it takes some friction out of managing a large number of templates.
|
||||
|
||||
When writing templates, one thing you can do is call out to other registered templates for small values. A good example might be some sort of list element. You can have a template that renders the list element, and then your template that renders your list can use the list element template in turn.
|
||||
|
||||
Use of another template within a template might look like this:
|
||||
|
||||
```
|
||||
<ul>
|
||||
{{ range .List }}
|
||||
{{ template "list_element" . }}
|
||||
{{ end }}
|
||||
</ul>
|
||||
```
|
||||
|
||||
You’re now rendering the `list_element` template with the list element from `.List`. But what if you want to also pass the current user `.User`? Unfortunately, you can only pass one argument from one template to another. If you have two arguments you want to pass to another template, with the standard library, you’re out of luck.
|
||||
|
||||
The [whtmpl][65] package adds three helper functions to aid you here, `makepair`, `makemap`, and `makeslice` (more docs under the [whtmpl.Collection][66] type). `makepair` is the simplest. It takes two arguments and constructs a [whtmpl.Pair][67]. Fixing our example above would look like this now:
|
||||
|
||||
```
|
||||
<ul>
|
||||
{{ $user := .User }}
|
||||
{{ range .List }}
|
||||
{{ template "list_element" (makepair . $user) }}
|
||||
{{ end }}
|
||||
</ul>
|
||||
```
|
||||
|
||||
The second thing [whtmpl][65] does is make defining lots of templates easy, by optionally automatically naming templates after the name of the file the template is defined in.
|
||||
|
||||
For example, say you have three files.
|
||||
|
||||
Here’s `pkg.go`:
|
||||
|
||||
```
|
||||
package views
|
||||
|
||||
import "gopkg.in/webhelp.v1/whtmpl"
|
||||
|
||||
var Templates = whtmpl.NewCollection()
|
||||
```
|
||||
|
||||
Here’s `landing.go`:
|
||||
|
||||
```
|
||||
package views
|
||||
|
||||
var _ = Templates.MustParse(`{{ template "header" . }}
|
||||
|
||||
<h1>Landing!</h1>`)
|
||||
```
|
||||
|
||||
And here’s `header.go`:
|
||||
|
||||
```
|
||||
package views
|
||||
|
||||
var _ = Templates.MustParse(`<title>My website!</title>`)
|
||||
```
|
||||
|
||||
Now, you can import your new `views` package and render the `landing` template this easily:
|
||||
|
||||
```
|
||||
func handler(w http.ResponseWriter, req *http.Request) {
|
||||
views.Templates.Render(w, req, "landing", map[string]interface{}{})
|
||||
}
|
||||
```
|
||||
|
||||
### User authentication
|
||||
|
||||
I’ve written two Webhelp-style authentication libraries that I end up using frequently.
|
||||
|
||||
The first is an OAuth2 library, [whoauth2][68]. I’ve written up [an example application that authenticates with Google, Facebook, and Github][69].
|
||||
|
||||
The second, [whgoth][70], is a wrapper around [markbates/goth][71]. My portion isn’t quite complete yet (some fixes are still necessary for optional App Engine support), but will support more non-OAuth2 authentication sources (like Twitter) when it is done.
|
||||
|
||||
### Route listing
|
||||
|
||||
Surprise! If you’ve used [webhelp][27] based handlers and middleware for your whole app, you automatically get route listing for free, via the [whroute][72] package.
|
||||
|
||||
My web serving code’s `main` method often has a form like this:
|
||||
|
||||
```
|
||||
switch flag.Arg(0) {
|
||||
case "serve":
|
||||
panic(whlog.ListenAndServe(*listenAddr, routes))
|
||||
case "routes":
|
||||
whroute.PrintRoutes(os.Stdout, routes)
|
||||
default:
|
||||
fmt.Printf("Usage: %s <serve|routes>\n", os.Args[0])
|
||||
}
|
||||
```
|
||||
|
||||
Here’s some example output:
|
||||
|
||||
```
|
||||
GET /auth/_cb/
|
||||
GET /auth/login/
|
||||
GET /auth/logout/
|
||||
GET /
|
||||
GET /account/apikeys/
|
||||
POST /account/apikeys/
|
||||
GET /project/<int>/
|
||||
GET /project/<int>/control/<int>/
|
||||
POST /project/<int>/control/<int>/sample/
|
||||
GET /project/<int>/control/
|
||||
Redirect: f(req)
|
||||
POST /project/<int>/control/
|
||||
POST /project/<int>/control_named/<string>/sample/
|
||||
GET /project/<int>/control_named/
|
||||
Redirect: f(req)
|
||||
GET /project/<int>/sample/<int>/
|
||||
GET /project/<int>/sample/<int>/similar[/<*>]
|
||||
GET /project/<int>/sample/
|
||||
Redirect: f(req)
|
||||
POST /project/<int>/search/
|
||||
GET /project/
|
||||
Redirect: /
|
||||
POST /project/
|
||||
```
|
||||
|
||||
### Other little things
|
||||
|
||||
[webhelp][27] has a number of other subpackages:
|
||||
|
||||
* [whparse][73] assists in parsing optional request arguments.
|
||||
* [whredir][74] provides some handlers and helper methods for doing redirects in various cases.
|
||||
* [whcache][75] creates request-specific mutable storage for caching various computations and database loaded data. Mutability helps helper functions that aren’t used as middleware share data.
|
||||
* [whfatal][76] uses panics to simplify early request handling termination. Probably avoid this package unless you want to anger other Go developers.
|
||||
|
||||
|
||||
|
||||
### Summary
|
||||
|
||||
Designing your web project as a collection of composable middlewares goes quite a long way to simplify your code design, eliminate cross-cutting concerns, and create a more flexible development environment. Use my [webhelp][27] package if it helps you.
|
||||
|
||||
Or don’t! Whatever! It’s still a free country last I checked.
|
||||
|
||||
#### Update
|
||||
|
||||
Peter Kieltyka points me to his [Chi framework][77], which actually does seem to do the right things with respect to middleware, handlers, and contexts - certainly much more so than all the other frameworks I’ve seen. So, shoutout to Peter and the team at Pressly!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.jtolio.com/2017/01/writing-advanced-web-applications-with-go
|
||||
|
||||
作者:[jtolio.com][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.jtolio.com/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ruby-lang.org/
|
||||
[2]: http://rubyonrails.org/
|
||||
[3]: http://www.sinatrarb.com/
|
||||
[4]: https://www.python.org/
|
||||
[5]: https://www.djangoproject.com/
|
||||
[6]: http://flask.pocoo.org/
|
||||
[7]: https://golang.org/
|
||||
[8]: https://groups.google.com/d/forum/golang-nuts
|
||||
[9]: https://www.reddit.com/r/golang/
|
||||
[10]: https://revel.github.io/
|
||||
[11]: https://gin-gonic.github.io/gin/
|
||||
[12]: http://iris-go.com/
|
||||
[13]: https://beego.me/
|
||||
[14]: https://go-macaron.com/
|
||||
[15]: https://github.com/go-martini/martini
|
||||
[16]: https://github.com/gocraft/web
|
||||
[17]: https://github.com/urfave/negroni
|
||||
[18]: https://godoc.org/goji.io
|
||||
[19]: https://echo.labstack.com/
|
||||
[20]: https://medium.com/code-zen/why-i-don-t-use-go-web-frameworks-1087e1facfa4
|
||||
[21]: https://groups.google.com/forum/#!topic/golang-nuts/R_lqsTTBh6I
|
||||
[22]: https://www.reddit.com/r/golang/comments/1yh6gm/new_to_go_trying_to_select_web_framework/
|
||||
[23]: https://golang.org/pkg/net/http/#Handler
|
||||
[24]: https://golang.org/pkg/net/http/#Request
|
||||
[25]: https://golang.org/pkg/net/http/#Request.Context
|
||||
[26]: https://golang.org/pkg/net/http/#Request.WithContext
|
||||
[27]: https://godoc.org/gopkg.in/webhelp.v1
|
||||
[28]: https://golang.org/doc/articles/wiki/
|
||||
[29]: https://expressjs.com/
|
||||
[30]: https://nodejs.org/en/
|
||||
[31]: https://en.wikipedia.org/wiki/Cross-cutting_concern
|
||||
[32]: https://github.com/gorilla/mux
|
||||
[33]: https://github.com/gorilla/
|
||||
[34]: https://golang.org/pkg/net/http/#ServeMux
|
||||
[35]: https://swtch.com/~rsc/
|
||||
[36]: https://github.com/rsc/tiddly
|
||||
[37]: https://github.com/rsc/tiddly/blob/8f9145ac183e374eb95d90a73be4d5f38534ec47/tiddly.go#L201
|
||||
[38]: https://godoc.org/gopkg.in/webhelp.v1/whmux#Dir
|
||||
[39]: https://godoc.org/gopkg.in/webhelp.v1/whmux
|
||||
[40]: https://godoc.org/gopkg.in/webhelp.v1/whmux#IntArg
|
||||
[41]: https://godoc.org/gopkg.in/webhelp.v1/whmux#StringArg
|
||||
[42]: https://golang.org/pkg/context/
|
||||
[43]: https://blog.golang.org/context
|
||||
[44]: https://godoc.org/golang.org/x/net/context
|
||||
[45]: https://godoc.org/gopkg.in/webhelp.v1#GenSym
|
||||
[46]: https://godoc.org/gopkg.in/webhelp.v1/whcompat
|
||||
[47]: https://godoc.org/gopkg.in/webhelp.v1/whcompat#DoneNotify
|
||||
[48]: https://godoc.org/gopkg.in/webhelp.v1/whcompat#CloseNotify
|
||||
[49]: https://godoc.org/gopkg.in/webhelp.v1/wherr
|
||||
[50]: https://godoc.org/gopkg.in/webhelp.v1/wherr#Handle
|
||||
[51]: https://godoc.org/gopkg.in/webhelp.v1/wherr#pkg-variables
|
||||
[52]: https://godoc.org/github.com/spacemonkeygo/errors
|
||||
[53]: https://godoc.org/github.com/spacemonkeygo/errors/errhttp
|
||||
[54]: https://github.com/zeebo/errs
|
||||
[55]: https://godoc.org/gopkg.in/webhelp.v1/whsess
|
||||
[56]: https://godoc.org/golang.org/x/crypto/nacl/secretbox
|
||||
[57]: https://godoc.org/gopkg.in/webhelp.v1/whlog
|
||||
[58]: https://godoc.org/gopkg.in/webhelp.v1/whlog#LogRequests
|
||||
[59]: https://godoc.org/gopkg.in/webhelp.v1/whlog#LogResponses
|
||||
[60]: https://godoc.org/gopkg.in/webhelp.v1/whlog#ListenAndServe
|
||||
[61]: https://godoc.org/gopkg.in/webhelp.v1/whmon
|
||||
[62]: https://godoc.org/gopkg.in/webhelp.v1/whgls
|
||||
[63]: https://godoc.org/github.com/jtolds/gls
|
||||
[64]: https://golang.org/pkg/html/template/
|
||||
[65]: https://godoc.org/gopkg.in/webhelp.v1/whtmpl
|
||||
[66]: https://godoc.org/gopkg.in/webhelp.v1/whtmpl#Collection
|
||||
[67]: https://godoc.org/gopkg.in/webhelp.v1/whtmpl#Pair
|
||||
[68]: https://godoc.org/gopkg.in/go-webhelp/whoauth2.v1
|
||||
[69]: https://github.com/go-webhelp/whoauth2/blob/v1/examples/group/main.go
|
||||
[70]: https://godoc.org/gopkg.in/go-webhelp/whgoth.v1
|
||||
[71]: https://github.com/markbates/goth
|
||||
[72]: https://godoc.org/gopkg.in/webhelp.v1/whroute
|
||||
[73]: https://godoc.org/gopkg.in/webhelp.v1/whparse
|
||||
[74]: https://godoc.org/gopkg.in/webhelp.v1/whredir
|
||||
[75]: https://godoc.org/gopkg.in/webhelp.v1/whcache
|
||||
[76]: https://godoc.org/gopkg.in/webhelp.v1/whfatal
|
||||
[77]: https://github.com/pressly/chi
|
||||
@@ -1,93 +0,0 @@
|
||||
What I learnt from building 3 high traffic web applications on an embedded key value store.
|
||||
============================================================
|
||||
|
||||
|
||||

|
||||
|
||||
I grew up as part of the fad that all web applications must be built on proper architecture, usually involving a well tested and stable sql database like postgres, or in some stretch cases a nosql database like mongodb. The reasons were simple:
|
||||
|
||||
* They can be scaled and scaled and scaled. Very important when your application gets it’s first one billion users overnight.
|
||||
|
||||
* SQL is the best way to access your data. And you always need a complicated message layer to access that data.
|
||||
|
||||
* If you don’t use sql, how will you perform joins? How will you build in complicated search? How will you prevent your code from going so large with complexity that it falls and kills your cat?
|
||||
|
||||
|
||||

|
||||
Your cat is scared of your code.
|
||||
|
||||
* Why build an application where you won’t need to employ an experienced database administrator to create your complicated schemas and perform the very important and complicated database maintenance tasks.
|
||||
|
||||
In 2015, I stumbled on the golang [BoltDB database library][1], and first used to save state in basic server-side applications. Then I stumbled on [blevesearch][2], and it dawned on me. The most basic thing most databases offer is storage, and flexible search over stored data. With boltdb, I could store my data efficiently, and with bleve I could search and access my data in interesting ways.
|
||||
|
||||

|
||||
blevesearch
|
||||
|
||||
On this basis, I built and launched [Calabar Yellow Pages][3] on just boltDB and blevesearch, and then when I saw how smoothly it worked and scaled in production, I went on to build [Shop440][4] (A merge between Shopify and Aliexpress) on badger DB (A much faster alternative to boltdb) and blevesearch for indexing.
|
||||
|
||||
### What I learnt:
|
||||
|
||||
### Key Value Stores are fast
|
||||
|
||||
Firstly, a NoSQL key value store is fast, much faster than a comparable relational database. Its speed comes from its simplicity. A key value database stores a data record using one primary key. The key means the record is uniquely identifiable, and can be directly accessed. Other than this, it’s up to the developer to architect any more complexity in accessing data.
|
||||
|
||||
|
||||

|
||||
|
||||
Also, since the key value stores I used were embedded databases, I was able to strike off some of the processing that comes with more established databases. In a database like mongoDB, A lot of resources and latency goes into serializing data and passing it over the wire to the client application which then deserializes and decodes it. This is a waste of resources, especially when the client and the database are running on the same server, and not on separate servers where they would benefit from communicating over http. An embedded database strikes out all these extra serialization and even tcp transport costs.
|
||||
|
||||
### Great Scalability — What About Consistency?
|
||||
|
||||
Secondly, a NoSQL key value database is also highly scalable. This too is a function of its simplicity. Unlike a relational database, a NoSQL key value database is not obliged to scale vertically. It can scale over several machines or devices by several orders of magnitude, without the need for significant redesign. Financially, this is a big advantage too.
|
||||
|
||||

|
||||
|
||||
But in my case, since using an embedded key value store, most scaling happened vertically and even sustained for a very long time. At the moment, even with a database size of almost a terabyte, the application is still holding strong. With badger, I came up with a strategy of having multiple badger databases, each representing a collection. This way, if I ever have a need to scale the system beyond a single server, I could isolate each individual database and it’s corresponding program logic into a separate micro-service. (I will write more about this in the future. )
|
||||
|
||||
### Cheap on time and effort
|
||||
|
||||
|
||||

|
||||
|
||||
Scaling a relational database solution often means that cost increases dis-proportionally; it also requires time and effort (and server downtime) to change your database schema. By comparison, a key value database keeps the price curve linear rather than exponential, and it is designed to handle data without any predefined schema. Some (not all) key value databases, like other NoSQL databases, are also designed for ‘eventual consistency’. This means that data are synchronized ‘at some point’ between copies on different machines, but not immediately.
|
||||
|
||||
### Schema-Free Rows and Columns
|
||||
|
||||

|
||||
|
||||
### A case for Embedded Key Value Stores
|
||||
|
||||
According to [https://www.sqlite.org/whentouse.html][5]
|
||||
|
||||
> SQLite works great as the database engine for most low to medium traffic websites (which is to say, most websites). The amount of web traffic that SQLite can handle depends on how heavily the website uses its database. Generally speaking, any site that gets fewer than 100K hits/day should work fine with SQLite. The 100K hits/day figure is a conservative estimate, not a hard upper bound. SQLite has been demonstrated to work with 10 times that amount of traffic.
|
||||
|
||||
> The SQLite website ([https://www.sqlite.org/][6]) uses SQLite itself, of course, and as of this writing (2015) it handles about 400K to 500K HTTP requests per day, about 15–20% of which are dynamic pages touching the database. Dynamic content uses [about 200 SQL statements per webpage][7]. This setup runs on a single VM that shares a physical server with 23 others and yet still keeps the load average below 0.1 most of the time.
|
||||
|
||||
Most embedded key value stores like badger are very very much more efficient and performant than sqlite, so this could go to show how much more is possible with embedded key value stores, if sqlite can handle so much hits per day.
|
||||
|
||||
Also, using an embedded key value store alongside an embedded indexing engine like Blevesearch in a compiled language like Golang, means you can truly deploy single binaries with no external dependencies and no need for post deployment setups.
|
||||
|
||||
### In a nutshell
|
||||
|
||||
Embedded key value stores give you all the advantages of a nosql database, but where it is lacking is in search, since you’re only able to query for items by their keys and key’s prefix. But when paired with an indexing engine like blevesearch in golang, or elasicsearch and lucene, the pair gives you a very capable database with features like full text search, location based search, etc in a very resource efficient package and scales well.
|
||||
|
||||
I believe this pair should have more popularity than it actually currently has, and go a long in reducing server expenses especially with applications which never expect facebook level scale.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://hackernoon.com/what-i-learnt-from-building-3-high-traffic-web-applications-on-an-embedded-key-value-store-68d47249774f
|
||||
|
||||
作者:[Anthony Alaribe ][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://hackernoon.com/@tonyalaribe?source=post_header_lockup
|
||||
[1]:https://github.com/boltdb/bolt
|
||||
[2]:https://github.com/blevesearch/bleve
|
||||
[3]:http://calabarpages.com/
|
||||
[4]:https://shop440.com/
|
||||
[5]:https://www.sqlite.org/whentouse.html
|
||||
[6]:https://www.sqlite.org/
|
||||
[7]:https://www.sqlite.org/np1queryprob.html
|
||||
@@ -1,103 +0,0 @@
|
||||
Sound themes in Linux: What every user should know
|
||||
======
|
||||
|
||||

|
||||
|
||||
Like all modern operating systems, Linux has a set of specifications for sound themes. Sound themes are sets of similar sounds coordinated into themes that sound good together. They signal events such as switching to a different workspace, opening a new application, plugging and unplugging hardware, and alerting you when your battery is low or fully charged. The sounds that play is determined by which themes you have installed and which ones you’re currently using. If your desktop tries to play a sound your theme doesn’t have, it will play a sound from another sound theme if it can find one.
|
||||
|
||||
### How do I select a sound theme?
|
||||
|
||||
This is where things can get tricky. Most desktops make it easy to select a sound theme, but some are more challenging, and others can't do this at all. I’ll detail the steps for [MATE][1], my personal favorite desktop. If you use another desktop, consult your desktop’s help by pressing F1.
|
||||
|
||||
#### MATE
|
||||
|
||||
To select a sound theme in MATE, open the sound settings by either pressing Alt+F1, or right-arrowing to the Settings menu, down-arrowing to Preferences, right-arrowing twice to the Hardware menu and selecting the Sound menu item. If you have a Search menu installed, such as MATE menu or [Linux Mint][2] menu, simply open that menu, search for "Sound settings," and press Enter on the first result. Once the dialog is opened, tab over to the "Sound theme" combo box and select your sound theme. If you want sounds for windows and buttons to play, check the checkbox; if not, clear it. Then press Close.
|
||||
|
||||
### How many sound themes are available in Linux?
|
||||
|
||||
There are several sound theme options in Linux, but most are included only in select distros because those distro’s developers made the theme. There is only one theme available in all distros by default, as I'll discuss later in this article. If you want a sound theme that's not available in your distribution, you'll need to download it and copy it into the proper place. For all users, this folder is `/usr/share/sounds`; for your own personal use, it is `~/.local/share/sounds`. Most sound themes can legally be used in any Linux distribution, including the sound themes in the [Ubuntu][3], [Linux Mint][4], [elementary OS][5] and [Trisquel][6] distributions.
|
||||
|
||||
### Where can I get sound themes?
|
||||
|
||||
There are several websites dedicated to sound themes, desktop background themes, icon sets, and more. These include:
|
||||
|
||||
#### Gnome-look
|
||||
|
||||
In my opinion, [Gnome-look.org][7] is the best site in terms of selection and variety. It hosts a wide variety of sound themes, icon sets, desktop themes, desktop backgrounds, and so on. Its name is misleading; the site works for all desktops, not just GNOME.
|
||||
|
||||
#### Mate-Look
|
||||
|
||||
[Mate-Look.org][8], another site specific to the MATE desktop, offers a smaller but still respectable collection of sound themes, icon sets, backgrounds, and more.
|
||||
|
||||
#### Linux a11y sound theme
|
||||
|
||||
This is the main sound theme for the Linux accessibility organization, and I maintain the site, which you can find at [Linux-a11y-sound-theme][9]. All these sounds are free to use, and the entire sound theme is open source. It is a bit of a hodgepodge mixture, but it sounds decent.
|
||||
|
||||
#### Chihuahua sound theme
|
||||
|
||||
This theme is composed of various adorable sounds my chihuahua-Pekinese mix makes, recorded on GNU/Linux using various programs and converted into the proper formats. This sound theme can be cloned at `git://github.com/coffeeking/chihuahua-sound-theme`.
|
||||
|
||||
#### Free desktop sound theme
|
||||
|
||||
Available in all Linux distributions, this sound theme is usually installed along with your desktop of choice. I don’t have a link for this sound theme, but it is usually available in your package manager as "sound-theme-freedesktop" or something similar. This theme is meant to demonstrate what themes can do rather than as an all-inclusive theme.
|
||||
|
||||
### The bad
|
||||
|
||||
While the selection of sound themes is quite good, there are some problems—not with the sound themes themselves, but with the knowledge of how sound themes work and how to create them. A common problem concerns people using sounds they are not legally allowed to use, such as the sounds included in Microsoft Windows operating systems. These sounds, which are licensed (usually by Creative Commons), are legal for use only with Windows; using them with Linux is illegal.
|
||||
|
||||
To address this problem, websites that offer sound themes should include clear guidelines specifying what is and is not legal to post. They should also include credit guidelines so those who create sound themes receive credit when others use their sounds. Many users don't know where to find high-quality sounds, so they use what they know. The solution is to make accessible websites offering a wide selection of sounds with clear licenses so users understand how they can and can't use these sounds.
|
||||
|
||||
I’ve detailed two such sites below, but these are not the only ones.
|
||||
|
||||
#### SoundBible
|
||||
|
||||
[SoundBible][10] offers a plethora of good sounds, most of which are the right length for short clip tasks, like desktop sounds. All are free, though not all are free to use commercially—check the specific license that comes with a sound for details.
|
||||
|
||||
#### Freesound
|
||||
|
||||
[Freesound][11] also provides a wide variety of sound effects for desktop tasks and other uses. Both Freesound and SoundBible include clear licenses as well as author credits so users know who made the sound and what they can do with it. This cuts down on confusion and accidental (or deliberate) misuse of sounds.
|
||||
|
||||
### Theme and naming specifications
|
||||
|
||||
A big problem in open source is that many users do not know how sound themes work or how to create them. To address this, below I will link to two specifications: sound theme specifications, which explain what should be included in a sound themes index.theme file (mandatory for all sound themes), and sound-naming guidelines, which detail how sounds should be named for your desktop to find and play them.
|
||||
|
||||
#### Sound theme specs
|
||||
|
||||
[This specification][12] explains what’s in a sound theme’s index file, which is the file that describes the sound theme and lists the theme's name, what files it contains, and so on. Click on the `html` link under "The Sound Theme Spec" heading (ignore the "draft" comment; this specification is stable and has become standardized).
|
||||
|
||||
#### Sound-naming specs
|
||||
|
||||
[This specification][12] explains how sounds should be named for your desktop to find and play them. Click on the `html` link under the "The Sound Naming Spec" heading (this also has a "draft" comment, and this theme is also standardized). The site also details what file format sounds should be in. If you come across a file that is not in a proper format, you can easily convert it using applications like [SoundConverter][13] and [FFmpeg][14].
|
||||
|
||||
### Conclusion
|
||||
|
||||
Like most things in open source, sound themes generally get little attention; most users don’t even notice that they are there. But for us visually impaired people and others who prefer unique computer experiences, sound themes provide a nice touch. Along with icon themes and desktop backgrounds, they showcase the talent and variety for which open source is famous. But it's important for users to understand how sound themes work and what they can and cannot do with them.
|
||||
|
||||
I hope this article has been helpful. Looking forward to the next big sound theme!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/6/sound-themes-linux
|
||||
|
||||
作者:[Kendell Clark][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/kendell-clark
|
||||
[1]:https://mate-desktop.org/themes/
|
||||
[2]:https://linuxmint.com/
|
||||
[3]:http://www.ubuntu.com/
|
||||
[4]:http://www.Linuxmint.com/
|
||||
[5]:http://www.elementaryos.org/
|
||||
[6]:http://www.trisquel.info/
|
||||
[7]:http://www.gnome-look.org/
|
||||
[8]:http://www.mate-look.org/
|
||||
[9]:http://www.github.com/coffeeking/Linux-a11y-sound-theme
|
||||
[10]:http://www.soundbible.com/
|
||||
[11]:http://www.freesound.org/
|
||||
[12]:https://www.freedesktop.org/wiki/Specifications/sound-theme-spec/
|
||||
[13]:http://www.soundconverter.org/
|
||||
[14]:http://www.ffmpeg.org/
|
||||
@@ -1,152 +0,0 @@
|
||||
Systemd Services: Monitoring Files and Directories
|
||||
======
|
||||
|
||||

|
||||
|
||||
So far in this systemd multi-part tutorial, we’ve covered [how to start and stop a service by hand][1], [how to start a service when booting your OS and have it stop on power down][2], and [how to boot a service when a certain device is detected][3]. This installment does something different yet again and covers how to create a unit that starts a service when something changes in the filesystem. For the practical example, you'll see how you can use one of these units to extend the [surveillance system we talked about last time][4].
|
||||
|
||||
### Where we left off
|
||||
|
||||
[Last time we saw how the surveillance system took pictures, but it did nothing with them][3]. In fact, it even overwrote the last picture it took when it detected movement so as not to fill the storage of the device.
|
||||
|
||||
Does that mean the system is useless? Not by a long shot. Because, you see, systemd offers yet another type of units, _paths_ , that can help you out. _Path_ units allow you to trigger a service when an event happens in the filesystem, say, when a file gets deleted or a directory accessed. And, overwriting an image is exactly the kind of event we are talking about here.
|
||||
|
||||
### Anatomy of a Path Unit
|
||||
|
||||
A systemd path unit takes the extension _.path_ , and it monitors a file or directory. A _.path_ unit calls another unit (usually a _.service_ unit with the same name) when something happens to the monitored file or directory. For example, if you have a _picchanged.path_ unit to monitor the snapshot from your webcam, you will also have a _picchanged.service_ that will execute a script when the snapshot is overwritten.
|
||||
|
||||
Path units contain a new section, `[Path]`, with few more directives. First, you have the what-to-watch-for directives:
|
||||
|
||||
* **`PathExists=`** monitors whether the file or directory exists. If it does, the associated unit gets triggered. `PathExistsGlob=` works in a similar fashion, but lets you use globbing, like when you use `ls *.jpg` to search for all the JPEG images in a directory. This lets you check, for example, whether a file with a certain extension exists.
|
||||
* **`PathChanged=`** watches a file or directory and activates the configured unit whenever it changes. It is not activated on every write to the watched file but only when a monitored file open for for writing is changed and then closed. The associated unit is executed when the file is closed.
|
||||
* **`PathModified=`** , on the other hand, does activate the unit when anything is changed in the file you are monitoring, even before you close the file.
|
||||
* **`DirectoryNotEmpty=`** does what it says on the box, that is, it activates the associated unit if the monitored directory contains files or subdirectories.
|
||||
|
||||
|
||||
|
||||
Then, we have `Unit=` that tells the _.path_ which _.service_ unit to activate, in case you want to give it a different name to that of your _.path_ unit; `MakeDirectory=` can be `true` or `false` (or `0` or `1`, or `yes` or `no`) and creates the directory you want to monitor before monitoring starts. Obviously, using `MakeDirectory=` in combination with `PathExists=` does not make sense. However, `MakeDirectory=` can be used in combination with `DirectoryMode=`, which you use to set the the mode (permissions) of the new directory. If you don't use `DirectoryMode=`, the default permissions for the new directory are `0755`.
|
||||
|
||||
### Building _picchanged.path_
|
||||
|
||||
All these directives are very useful, but you will be just looking for changes made to one single file, so your _.path_ unit is very simple:
|
||||
|
||||
```
|
||||
#picchanged.path
|
||||
[Unit]
|
||||
Wants= webcam.service
|
||||
|
||||
[Path]
|
||||
PathChanged= /home/[user name]/monitor/monitor.jpg
|
||||
```
|
||||
|
||||
In the `Unit=` section the line that says
|
||||
|
||||
```
|
||||
Wants= webcam.service
|
||||
```
|
||||
|
||||
The `Wants=` directive is the preferred way of starting up a unit the current unit needs to work properly. [`webcam.service` is the name you gave the surveillance service that you saw in the previous article][3] and is the service that actually controls the webcam and makes it take a snap every half second. This means it’s _picchanged.path_ that is going to start up _webcam.service_ now, and not the [Udev rule you saw in the prior article][3]. You will use the Udev rule to start _picchanged.path_ instead.
|
||||
|
||||
To summarize: the Udev rule pulls in your new _picchanged.path_ unit, which, in turn pulls in the _webcam.service_ as a requirement for everything to work perfectly.
|
||||
|
||||
The "thing" that _picchanged.path_ monitors is the _monitor.jpg_ file in the _monitor/_ directory in your home directory. As you saw last time, _webcam.service_ called a script, _checkimage.sh_ , took a picture at the beginning of its execution and stored it in _monitor/temp.jpg_. _checkimage.sh_ then took another pic, _temp.jpg_ , and compared it with _monitor.jpg_. If it found significant differences (like when somebody walks into frame) the script overwrote _monitor.jpg_ with the _temp.jpg_. That is when _picchanged.path_ fires.
|
||||
|
||||
As you haven't included a `Unit=` directive in your _.path_ , the unit systemd expects a matching _picchanged.service_ unit which it will trigger when _/home/[ _user name_ ]/monitor/monitor.jpg_ gets modified:
|
||||
|
||||
```
|
||||
#picchanged.service
|
||||
[Service]
|
||||
Type= simple
|
||||
ExecStart= /home/[user name]/bin/picmonitor.sh
|
||||
```
|
||||
|
||||
For the time being, let’s make _picmonitor.sh_ save a time-stamped copy of _monitor.jpg_ every time changes get detected:
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
# This is the pcmonitor.sh script
|
||||
|
||||
cp /home/[user name]/monitor/monitor.jpg /home/[user name]/monitor/"`date`.jpg"
|
||||
```
|
||||
|
||||
### Udev Changes
|
||||
|
||||
You have to change the custom Udev rule you wrote in [the previous installment][3] so everything works. Edit _/etc/udev/rules.d/01-webcam.rules_ so instead of looking like this:
|
||||
|
||||
```
|
||||
ACTION=="add", SUBSYSTEM=="video4linux", ATTRS{idVendor}=="03f0",
|
||||
ATTRS{idProduct}=="e207", SYMLINK+="mywebcam", TAG+="systemd",
|
||||
MODE="0666", ENV{SYSTEMD_WANTS}="webcam.service"
|
||||
```
|
||||
|
||||
It looks like this:
|
||||
|
||||
```
|
||||
ACTION=="add", SUBSYSTEM=="video4linux", ATTRS{idVendor}=="03f0",
|
||||
ATTRS{idProduct}=="e207", SYMLINK+="mywebcam", TAG+="systemd",
|
||||
MODE="0666", ENV{SYSTEMD_WANTS}="picchanged.path"
|
||||
```
|
||||
|
||||
The new rule, instead of calling _webcam.service_ , now calls _picchanged.path_ when your webcam gets detected. (Note that you will have to change the `idVendor` and `IdProduct` to those of your own webcam -- you saw how to find these out previously).
|
||||
|
||||
For the record, I also changed _checkimage.sh_ from using PNG to JPEG images. I did this because I found some dependency problems with PNG images when working with _mplayer_ on some versions of Debian. _checkimage.sh_ now looks like this:
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
mplayer -vo jpeg -frames 1 tv:// -tv driver=v4l2:width=640:height=480:device=
|
||||
/dev/mywebcam &>/dev/null
|
||||
mv 00000001.jpg /home/paul/monitor/monitor.jpg
|
||||
|
||||
while true
|
||||
do
|
||||
mplayer -vo jpeg -frames 1 tv:// -tv driver=v4l2:width=640:height=480:device=
|
||||
/dev/mywebcam &>/dev/null
|
||||
mv 00000001.jpg /home/paul/monitor/temp.jpg
|
||||
|
||||
imagediff=`compare -metric mae /home/paul/monitor/monitor.jpg
|
||||
/home/paul/monitor/temp.jpg /home/paul/monitor/diff.png 2>&1 >
|
||||
/dev/null | cut -f 1 -d " "`
|
||||
|
||||
if [ `echo "$imagediff > 700.0" | bc` -eq 1 ]
|
||||
then
|
||||
mv /home/paul/monitor/temp.jpg /home/paul/monitor/monitor.jpg
|
||||
fi
|
||||
|
||||
sleep 0.5
|
||||
done
|
||||
```
|
||||
|
||||
### Firing up
|
||||
|
||||
This is a multi-unit service that, when all its bits and pieces are in place, you don't have to worry much about: you plug in the designated webcam (or boot the machine with the webcam already connected), _picchanged.path_ gets started thanks to the Udev rule and takes over, bringing up the _webcam.service_ and starting to check on the snaps. There is nothing else you need to do.
|
||||
|
||||
### Conclusion
|
||||
|
||||
Having the process split into two doesn't only help explain how path units work, but it’s also very useful for debugging. One service does not "touch" the other in any way, which means that you could, for example, improve the "motion detection" part, and it would be very easy to roll back if things didn't work as expected.
|
||||
|
||||
Admittedly, the example is a bit goofy, as there are definitely [better ways of monitoring movement using a webcam][5]. But remember: the main aim of these articles is to help you learn how systemd units work within a context.
|
||||
|
||||
Next time, we'll finish up with systemd units by looking at some of the other types of units available and show how to improve your home-monitoring system further by setting up service that sends images to another machine.
|
||||
|
||||
Learn more about Linux through the free ["Introduction to Linux" ][6]course from The Linux Foundation and edX.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/blog/learn/intro-to-linux/2018/6/systemd-services-monitoring-files-and-directories
|
||||
|
||||
作者:[Paul Brown][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.linux.com/users/bro66
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.linux.com/blog/learn/intro-to-linux/2018/5/writing-systemd-services-fun-and-profit
|
||||
[2]: https://www.linux.com/blog/learn/2018/5/systemd-services-beyond-starting-and-stopping
|
||||
[3]: https://www.linux.com/blog/intro-to-linux/2018/6/systemd-services-reacting-change
|
||||
[4]: https://www.linux.com/blog/learn/intro-to-linux/2018/6/systemd-services-monitoring-files-and-directories
|
||||
[5]: https://www.linux.com/learn/how-operate-linux-spycams-motion
|
||||
[6]: https://training.linuxfoundation.org/linux-courses/system-administration-training/introduction-to-linux
|
||||
@@ -1,254 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Three Ways To Reset And Change Forgotten Root Password on RHEL 7/CentOS 7 Systems)
|
||||
[#]: via: (https://www.2daygeek.com/linux-reset-change-forgotten-root-password-in-rhel-7-centos-7/)
|
||||
[#]: author: (Prakash Subramanian https://www.2daygeek.com/author/prakash/)
|
||||
|
||||
Three Ways To Reset And Change Forgotten Root Password on RHEL 7/CentOS 7 Systems
|
||||
======
|
||||
|
||||
If you are forget to remember your root password for RHEL 7 and CentOS 7 systems and want to reset the forgotten root password?
|
||||
|
||||
If so, don’t worry we are here to help you out on this.
|
||||
|
||||
Navigate to the following link if you want to **[reset forgotten root password on RHEL 6/CentOS 6][1]**.
|
||||
|
||||
This is generally happens when you use different password in vast environment or if you are not maintaining the proper inventory.
|
||||
|
||||
Whatever it is. No issues, we will help you through this article.
|
||||
|
||||
It can be done in many ways but we are going to show you the best three methods which we tried many times for our clients.
|
||||
|
||||
In Linux servers there are three different users are available. These are, Normal User, System User and Super User.
|
||||
|
||||
As everyone knows the Root user is known as super user in Linux and Administrator is in Windows.
|
||||
|
||||
We can’t perform any major activity without root password so, make sure you should have the right root password when you perform any major tasks.
|
||||
|
||||
If you don’t know or don’t have it, try to reset using one of the below method.
|
||||
|
||||
* Reset Forgotten Root Password By Booting into Single User Mode using `rd.break`
|
||||
* Reset Forgotten Root Password By Booting into Single User Mode using `init=/bin/bash`
|
||||
* Reset Forgotten Root Password By Booting into Rescue Mode
|
||||
|
||||
|
||||
|
||||
### Method-1: Reset Forgotten Root Password By Booting into Single User Mode
|
||||
|
||||
Just follow the below procedure to reset the forgotten root password in RHEL 7/CentOS 7 systems.
|
||||
|
||||
To do so, reboot your system and follow the instructions carefully.
|
||||
|
||||
**`Step-1:`** Reboot your system and interrupt at the boot menu by hitting **`e`** key to modify the kernel arguments.
|
||||
![][3]
|
||||
|
||||
**`Step-2:`** In the GRUB options, find `linux16` word and add the `rd.break` word in the end of the file then press `Ctrl+x` or `F10` to boot into single user mode.
|
||||
![][4]
|
||||
|
||||
**`Step-3:`** At this point of time, your root filesystem will be mounted in Read only (RO) mode to /sysroot. Run the below command to confirm this.
|
||||
|
||||
```
|
||||
# mount | grep root
|
||||
```
|
||||
|
||||
![][5]
|
||||
|
||||
**`Step-4:`** Based on the above output, i can say that i’m in single user mode and my root file system is mounted in read only mode.
|
||||
|
||||
It won’t allow you to make any changes on your system until you mount the root filesystem with Read and write (RW) mode to /sysroot. To do so, use the following command.
|
||||
|
||||
```
|
||||
# mount -o remount,rw /sysroot
|
||||
```
|
||||
|
||||
![][6]
|
||||
|
||||
**`Step-5:`** Currently your file systems are mounted as a temporary partition. Now, your command prompt shows **switch_root:/#**.
|
||||
|
||||
Run the following command to get into a chroot jail so that /sysroot is used as the root of the file system.
|
||||
|
||||
```
|
||||
# chroot /sysroot
|
||||
```
|
||||
|
||||
![][7]
|
||||
|
||||
**`Step-6:`** Now you can able to reset the root password with help of `passwd` command.
|
||||
|
||||
```
|
||||
# echo "CentOS7$#123" | passwd --stdin root
|
||||
```
|
||||
|
||||
![][8]
|
||||
|
||||
**`Step-7:`** By default CentOS 7/RHEL 7 use SELinux in enforcing mode, so create a following hidden file which will automatically perform a relabel of all files on next boot.
|
||||
|
||||
It allow us to fix the context of the **/etc/shadow** file.
|
||||
|
||||
```
|
||||
# touch /.autorelabel
|
||||
```
|
||||
|
||||
![][9]
|
||||
|
||||
**`Step-8:`** Issue `exit` twice to exit from the chroot jail environment and reboot the system.
|
||||
![][10]
|
||||
|
||||
**`Step-9:`** Now you can login to your system with your new password.
|
||||
![][11]
|
||||
|
||||
### Method-2: Reset Forgotten Root Password By Booting into Single User Mode
|
||||
|
||||
Alternatively we can use the below procedure to reset the forgotten root password in RHEL 7/CentOS 7 systems.
|
||||
|
||||
**`Step-1:`** Reboot your system and interrupt at the boot menu by hitting **`e`** key to modify the kernel arguments.
|
||||
![][3]
|
||||
|
||||
**`Step-2:`** In the GRUB options, find `rhgb quiet` word and replace with the `init=/bin/bash` or `init=/bin/sh` word then press `Ctrl+x` or `F10` to boot into single user mode.
|
||||
|
||||
Screenshot for **`init=/bin/bash`**.
|
||||
![][12]
|
||||
|
||||
Screenshot for **`init=/bin/sh`**.
|
||||
![][13]
|
||||
|
||||
**`Step-3:`** At this point of time, your root system will be mounted in Read only mode to /. Run the below command to confirm this.
|
||||
|
||||
```
|
||||
# mount | grep root
|
||||
```
|
||||
|
||||
![][14]
|
||||
|
||||
**`Step-4:`** Based on the above ouput, i can say that i’m in single user mode and my root file system is mounted in read only (RO) mode.
|
||||
|
||||
It won’t allow you to make any changes on your system until you mount the root file system with Read and write (RW) mode. To do so, use the following command.
|
||||
|
||||
```
|
||||
# mount -o remount,rw /
|
||||
```
|
||||
|
||||
![][15]
|
||||
|
||||
**`Step-5:`** Now you can able to reset the root password with help of `passwd` command.
|
||||
|
||||
```
|
||||
# echo "RHEL7$#123" | passwd --stdin root
|
||||
```
|
||||
|
||||
![][16]
|
||||
|
||||
**`Step-6:`** By default CentOS 7/RHEL 7 use SELinux in enforcing mode, so create a following hidden file which will automatically perform a relabel of all files on next boot.
|
||||
|
||||
It allow us to fix the context of the **/etc/shadow** file.
|
||||
|
||||
```
|
||||
# touch /.autorelabel
|
||||
```
|
||||
|
||||
![][17]
|
||||
|
||||
**`Step-7:`** Finally `Reboot` the system.
|
||||
|
||||
```
|
||||
# exec /sbin/init 6
|
||||
```
|
||||
|
||||
![][18]
|
||||
|
||||
**`Step-9:`** Now you can login to your system with your new password.
|
||||
![][11]
|
||||
|
||||
### Method-3: Reset Forgotten Root Password By Booting into Rescue Mode
|
||||
|
||||
Alternatively, we can reset the forgotten Root password for RHEL 7 and CentOS 7 systems using Rescue mode.
|
||||
|
||||
**`Step-1:`** Insert the bootable media through USB or DVD drive which is compatible for you and reboot your system. It will take to you to the below screen.
|
||||
|
||||
Hit `Troubleshooting` to launch the `Rescue` mode.
|
||||
![][19]
|
||||
|
||||
**`Step-2:`** Choose `Rescue a CentOS system` and hit `Enter` button.
|
||||
![][20]
|
||||
|
||||
**`Step-3:`** Here choose `1` and the rescue environment will now attempt to find your Linux installation and mount it under the directory `/mnt/sysimage`.
|
||||
![][21]
|
||||
|
||||
**`Step-4:`** Simple hit `Enter` to get a shell.
|
||||
![][22]
|
||||
|
||||
**`Step-5:`** Run the following command to get into a chroot jail so that /mnt/sysimage is used as the root of the file system.
|
||||
|
||||
```
|
||||
# chroot /mnt/sysimage
|
||||
```
|
||||
|
||||
![][23]
|
||||
|
||||
**`Step-6:`** Now you can able to reset the root password with help of **passwd** command.
|
||||
|
||||
```
|
||||
# echo "RHEL7$#123" | passwd --stdin root
|
||||
```
|
||||
|
||||
![][24]
|
||||
|
||||
**`Step-7:`** By default CentOS 7/RHEL 7 use SELinux in enforcing mode, so create a following hidden file which will automatically perform a relabel of all files on next boot.
|
||||
It allow us to fix the context of the /etc/shadow file.
|
||||
|
||||
```
|
||||
# touch /.autorelabel
|
||||
```
|
||||
|
||||
![][25]
|
||||
|
||||
**`Step-8:`** Remove the bootable media then initiate the reboot.
|
||||
|
||||
**`Step-9:`** Issue `exit` twice to exit from the chroot jail environment and reboot the system.
|
||||
![][26]
|
||||
|
||||
**`Step-10:`** Now you can login to your system with your new password.
|
||||
![][11]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/linux-reset-change-forgotten-root-password-in-rhel-7-centos-7/
|
||||
|
||||
作者:[Prakash Subramanian][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/prakash/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/linux-reset-change-forgotten-root-password-in-rhel-6-centos-6/
|
||||
[2]: data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[3]: https://www.2daygeek.com/wp-content/uploads/2018/12/reset-forgotten-root-password-on-rhel-7-centos-7-2.png
|
||||
[4]: https://www.2daygeek.com/wp-content/uploads/2018/12/reset-forgotten-root-password-on-rhel-7-centos-7-3.png
|
||||
[5]: https://www.2daygeek.com/wp-content/uploads/2018/12/reset-forgotten-root-password-on-rhel-7-centos-7-5.png
|
||||
[6]: https://www.2daygeek.com/wp-content/uploads/2018/12/reset-forgotten-root-password-on-rhel-7-centos-7-6.png
|
||||
[7]: https://www.2daygeek.com/wp-content/uploads/2018/12/reset-forgotten-root-password-on-rhel-7-centos-7-8.png
|
||||
[8]: https://www.2daygeek.com/wp-content/uploads/2018/12/reset-forgotten-root-password-on-rhel-7-centos-7-10.png
|
||||
[9]: https://www.2daygeek.com/wp-content/uploads/2018/12/reset-forgotten-root-password-on-rhel-7-centos-7-10a.png
|
||||
[10]: https://www.2daygeek.com/wp-content/uploads/2018/12/reset-forgotten-root-password-on-rhel-7-centos-7-11.png
|
||||
[11]: https://www.2daygeek.com/wp-content/uploads/2018/12/reset-forgotten-root-password-on-rhel-7-centos-7-12.png
|
||||
[12]: https://www.2daygeek.com/wp-content/uploads/2018/12/method-reset-forgotten-root-password-on-rhel-7-centos-7-1.png
|
||||
[13]: https://www.2daygeek.com/wp-content/uploads/2018/12/method-reset-forgotten-root-password-on-rhel-7-centos-7-1a.png
|
||||
[14]: https://www.2daygeek.com/wp-content/uploads/2018/12/method-reset-forgotten-root-password-on-rhel-7-centos-7-3.png
|
||||
[15]: https://www.2daygeek.com/wp-content/uploads/2018/12/method-reset-forgotten-root-password-on-rhel-7-centos-7-4.png
|
||||
[16]: https://www.2daygeek.com/wp-content/uploads/2018/12/method-reset-forgotten-root-password-on-rhel-7-centos-7-5.png
|
||||
[17]: https://www.2daygeek.com/wp-content/uploads/2018/12/method-reset-forgotten-root-password-on-rhel-7-centos-7-6.png
|
||||
[18]: https://www.2daygeek.com/wp-content/uploads/2018/12/method-reset-forgotten-root-password-on-rhel-7-centos-7-7.png
|
||||
[19]: https://www.2daygeek.com/wp-content/uploads/2018/12/rescue-reset-forgotten-root-password-on-rhel-7-centos-7-1.png
|
||||
[20]: https://www.2daygeek.com/wp-content/uploads/2018/12/rescue-reset-forgotten-root-password-on-rhel-7-centos-7-2.png
|
||||
[21]: https://www.2daygeek.com/wp-content/uploads/2018/12/rescue-reset-forgotten-root-password-on-rhel-7-centos-7-3.png
|
||||
[22]: https://www.2daygeek.com/wp-content/uploads/2018/12/rescue-reset-forgotten-root-password-on-rhel-7-centos-7-4.png
|
||||
[23]: https://www.2daygeek.com/wp-content/uploads/2018/12/rescue-reset-forgotten-root-password-on-rhel-7-centos-7-5.png
|
||||
[24]: https://www.2daygeek.com/wp-content/uploads/2018/12/rescue-reset-forgotten-root-password-on-rhel-7-centos-7-6.png
|
||||
[25]: https://www.2daygeek.com/wp-content/uploads/2018/12/rescue-reset-forgotten-root-password-on-rhel-7-centos-7-7.png
|
||||
[26]: https://www.2daygeek.com/wp-content/uploads/2018/12/rescue-reset-forgotten-root-password-on-rhel-7-centos-7-8.png
|
||||
@@ -1,161 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (4 secrets management tools for Git encryption)
|
||||
[#]: via: (https://opensource.com/article/19/2/secrets-management-tools-git)
|
||||
[#]: author: (Austin Dewey https://opensource.com/users/adewey)
|
||||
|
||||
4 secrets management tools for Git encryption
|
||||
======
|
||||
See how Git-crypt, BlackBox, SOPS, and Transcrypt stack up for storing secrets in Git.
|
||||

|
||||
|
||||
There are a lot of great open source tools out there for storing secrets in Git. It can be hard to determine the right one for you and your organization—it depends on your use cases and requirements. To help you compare and choose, we'll look at four of the most popular open source tools for secrets management and see how they stack up against each other:
|
||||
|
||||
+ [Git-crypt](https://github.com/AGWA/git-crypt)
|
||||
+ [BlackBox](https://github.com/StackExchange/blackbox)
|
||||
+ [SOPS](https://github.com/mozilla/sops)
|
||||
+ [Transcrypt](https://github.com/elasticdog/transcrypt)
|
||||
|
||||
|
||||
We won't review larger solutions like HashiCorp Vault. A production-ready Vault can be a rather large hurdle, especially if your organization is just getting started with secrets management. The tools above are easy to use and set up quickly.
|
||||
|
||||
### Encryption types
|
||||
|
||||
These secrets management tools use GNU Privacy Guard ([GPG][1]), symmetric key encryption, and/or cloud key services.
|
||||
|
||||
* GPG-based tools require users to create a GPG keypair. The public key is used to encrypt and is shared with other users, while the private key is used to decrypt and is known by only one user.
|
||||
* Symmetric key tools are password-based and work when given the correct password.
|
||||
* Cloud key services—Amazon Key Management Service (KMS), Google Cloud KMS, and Azure Key Vault-based tools—allow integration with services from cloud providers.
|
||||
|
||||
|
||||
|
||||
The encryption types these secrets management tools use are:
|
||||
| | GPG | Symmetric key | Amazon KMS | Google KMS | Azure Key Vault |
|
||||
| Git-crypt | X | X | | | |
|
||||
| BlackBox | X | | | | |
|
||||
| SOPS | X | | X | X | X |
|
||||
| Transcrypt | | X | | | |
|
||||
|
||||
As you can see, Git-crypt and SOPS use more than one encryption basis. This means Git-crypt can achieve encryption by using GPG OR a symmetric key, and SOPS can use GPG OR one of the cloud services.
|
||||
|
||||
### Goals
|
||||
|
||||
These tools have similar goals:
|
||||
|
||||
| | Transparency with Git | Whole-file encryption | Partial-file encryption |
|
||||
| Git-crypt | X | X | |
|
||||
| BlackBox | X | X | |
|
||||
| SOPS | | X | X |
|
||||
| Transcrypt | X | X | |
|
||||
|
||||
All but SOPS are transparent with Git, which means they have built-in mechanisms to ensure that files in source control are encrypted without much effort from users. They help prevent a **git push** from accidentally pushing plaintext secrets to Git.
|
||||
|
||||
At this point, you might be wondering, "Why is SOPS here if it doesn't encrypt transparently with Git? Isn't this a post about Git encryption tools?" The reason is because of how it handles key-value-based files, such as YAML and JSON. When encrypting these types of files, SOPS will leave the keys unencrypted but will encrypt the values. There are often cases, especially in configuration management, where these types of files need to be encrypted in Git, but it would also be convenient to see what kind of information they contain. While SOPS does not provide native Git transparency, tools like [git-secrets][2] can be used alongside SOPS to help ensure plaintext secrets are not pushed to Git.
|
||||
|
||||
Finally, all of these tools support whole-file encryption, in which secrets files are encrypted in their entirety.
|
||||
|
||||
### Workflows and differences
|
||||
|
||||
None of these tools are particularly difficult to use, but they all have quirks and operational challenges to consider.
|
||||
|
||||
#### GPG
|
||||
|
||||
The basic workflow for a GPG-based tool is:
|
||||
|
||||
1. Initialize the repository with the encryption tool
|
||||
2. Create GPG keys for users that are allowed to manage secret files
|
||||
3. Add the corresponding public keys to the tool
|
||||
4. Designate the files that should be treated as "secret"
|
||||
5. Encrypt the files using the tool
|
||||
6. Repeat steps 2, 3, and 5 for each new user that is granted permission
|
||||
7. To revoke a user, remove the user and repeat step 5
|
||||
|
||||
|
||||
|
||||
In theory, this workflow is simple. One operational issue is GPG key maintenance. Your team will need to back up its GPG keys to prevent a lock-out scenario if GPG keys are accidentally deleted. If you are using the tool for automation, you will also need to consider who will be responsible for creating and maintaining keys for that service. Additionally, if you need to add, remove, or rotate a key, you'll need to re-encrypt each file for the changes to take effect.
|
||||
|
||||
Advantages and disadvantages of the GPG-based tools include:
|
||||
|
||||
* Git-crypt cannot remove GPG users natively, which means step 7 above is not easy to do. There are workarounds available, but it's not a built-in feature.
|
||||
* Git-crypt will transparently perform step 5 above on a **git push** —even when new users are added.
|
||||
* BlackBox provides a **blackbox_update_all_files** command, which can perform step 5 by re-encrypting all secret files in one command. This comes in handy in key rotation and adding/removing GPG keys, where all files need to be re-encrypted.
|
||||
* SOPS makes key rotation and adding/removing GPG keys the most difficult, as it requires each file to be manually re-encrypted.
|
||||
* BlackBox has a **blackbox_list_admins** command that returns the email address that corresponds with registered users' GPG keys. This makes it easier to discern who has access to the secrets versus trying to map plain GPG fingerprints.
|
||||
|
||||
|
||||
|
||||
#### Cloud key services
|
||||
|
||||
Here is a sample workflow using SOPS with Amazon KMS:
|
||||
|
||||
1. Create identity and access management (IAM) entities
|
||||
2. Create KMS master key
|
||||
3. Grant IAM entities access to the master key
|
||||
4. Add the master key to each secret file with SOPS and encrypt the file (adding keys and encrypting is usually a one-step process with SOPS)
|
||||
5. Re-encrypt when adding or removing master keys
|
||||
|
||||
|
||||
|
||||
Of these four tools, SOPS is the only one that allows users to configure encryption with a cloud-based key service. Cloud key services ease much of the operational burden that GPG-based solutions carry. Take Amazon KMS, for example: The master key is added to SOPS and access to secret files is controlled through IAM policies. Adding and removing users is as simple as granting or revoking permission with IAM, meaning secret files do not need to be re-encrypted when changing permissions, since nothing changed from SOPS's perspective. This solution does have its own set of operational challenges, however. Each member of the team must have an AWS account before they can access secret files. Also, admins must configure and maintain the IAM policies and KMS master key(s).
|
||||
|
||||
#### Symmetric key encryption
|
||||
|
||||
The workflow for symmetric key-based tools is probably the simplest:
|
||||
|
||||
1. Initialize the repository with the encryption tool
|
||||
2. Designate files that should be treated as "secret"
|
||||
3. **git push** to transparently encrypt the files
|
||||
4. Share the symmetric key with other users who need access
|
||||
5. Rotate the key each time a user is revoked access
|
||||
|
||||
|
||||
|
||||
Git-crypt and Transcrypt both provide a complex password as the symmetric key. The operational challenges are to find a secure way to share the symmetric key and to be sure to rotate the key each time a user is removed.
|
||||
|
||||
Here are a few differences between Git-crypt and Transcrypt, our symmetric key-compatible tools:
|
||||
|
||||
* Git-crypt is compatible with both GPG and symmetric key encryption
|
||||
* Git-crypt doesn't support symmetric key rotation, so you can't complete step 5 if you use it with a symmetric key
|
||||
* Transcrypt provides a convenient **\--rekey** command for key rotation
|
||||
|
||||
|
||||
|
||||
### Other features
|
||||
|
||||
Other features and characteristics of the tools include:
|
||||
|
||||
| | Editor-in-place | Auditing | Repo-level permission | File-level permission |
|
||||
| Git-crypt | | | X | |
|
||||
| BlackBox | X | | X | |
|
||||
| SOPS | X | X | | X |
|
||||
| Transcrypt | | | X | |
|
||||
|
||||
Both BlackBox and SOPS feature an "editor-in-place" tool, which decrypts the file and opens a text editor specified by the **$EDITOR** environment variable. This enables the user to make in-place edits to the file before it is saved and re-encrypted, so users can modify secret files without requiring them to be "decrypted in place" first.
|
||||
|
||||
SOPS is the only tool that provides an auditing feature. This feature tracks and monitors SOPS usage by forwarding events to a database. It requires a certain amount of setup, so check out SOPS's [README][3] for more information.
|
||||
|
||||
Git-crypt, BlackBox, and Transcrypt handle access at the repo level, meaning that if you can view one decrypted file, you can view them all. Depending on your use case, this is either a feature or a misfeature. SOPS handles permissions at the file level, meaning just because users can view one file, they can't necessarily view other files in the repo.
|
||||
|
||||
### For more information
|
||||
|
||||
Hopefully, this high-level overview of four open source secrets management tools will help you make an educated decision about the right tool for you. For more information on the tools, please check out their GitHub pages (linked at the top of this article).
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/2/secrets-management-tools-git
|
||||
|
||||
作者:[Austin Dewey][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/adewey
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.gnupg.org/
|
||||
[2]: https://github.com/awslabs/git-secrets
|
||||
[3]: https://github.com/mozilla/sops/blob/master/README.rst#auditing
|
||||
@@ -1,141 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Program the real world using Rust on Raspberry Pi)
|
||||
[#]: via: (https://opensource.com/article/19/3/physical-computing-rust-raspberry-pi)
|
||||
[#]: author: (Rahul Thakoor https://opensource.com/users/rahul27)
|
||||
|
||||
Program the real world using Rust on Raspberry Pi
|
||||
======
|
||||
|
||||
rust_gpizero uses the Rust programming language to do physical computing on the Raspberry Pi.
|
||||
|
||||
![][1]
|
||||
|
||||
If you own a Raspberry Pi, chances are you may already have experimented with physical computing—writing code to interact with the real, physical world, like blinking some LEDs or [controlling a servo motor][2]. You may also have used [GPIO Zero][3], a Python library that provides a simple interface to GPIO devices from Raspberry Pi with a friendly Python API. GPIO Zero is developed by [Opensource.com][4] community moderator [Ben Nuttall][5].
|
||||
|
||||
I am working on [**rust_gpiozero**][6], a port of the awesome GPIO Zero library that uses the Rust programming language. It is still a work in progress, but it already includes some useful components.
|
||||
|
||||
[Rust][7] is a systems programming language developed at Mozilla. It is focused on performance, reliability, and productivity. The Rust website has [great resources][8] if you'd like to learn more about it.
|
||||
|
||||
### Getting started
|
||||
|
||||
Before starting with rust_gpiozero, it's smart to have a basic grasp of the Rust programming language. I recommend working through at least the first three chapters in [The Rust Programming Language][9] book.
|
||||
|
||||
I recommend [installing Rust][10] on your Raspberry Pi using [**rustup**][11]. Alternatively, you can set up a cross-compilation environment using [cross][12] (which works only on an x86_64 Linux host) or [this how-to][13].
|
||||
|
||||
After you've installed Rust, create a new Rust project by entering:
|
||||
|
||||
```
|
||||
cargo new rust_gpiozero_demo
|
||||
```
|
||||
|
||||
Add **rust_gpiozero** as a dependency (currently in v0.2.0) by adding the following to the dependencies section in your **Cargo.toml** file
|
||||
|
||||
```
|
||||
rust_gpiozero = "0.2.0"
|
||||
```
|
||||
|
||||
Next, blink an LED—the "hello world" of physical computing by modifying the **main.rs** file with the following:
|
||||
```
|
||||
use rust_gpiozero::*;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
// Create a new LED attached to Pin 17
|
||||
let led = LED::new(17);
|
||||
|
||||
// Blink the LED 5 times
|
||||
for _ in 0.. 5{
|
||||
led.on();
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
led.off();
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
rust_gpiozero provides an easier interface for blinking an LED. You can use the blink method, providing the number of seconds it should stay on and off. This simplifies the code to the following:
|
||||
```
|
||||
use rust_gpiozero::*;
|
||||
fn main() {
|
||||
// Create a new LED attached to Pin 17
|
||||
let mut led = LED::new(17);
|
||||
|
||||
// on_time = 2 secs, off_time=3 secs
|
||||
led.blink(2.0,3.0);
|
||||
|
||||
// prevent program from exiting immediately
|
||||
led.wait();
|
||||
}
|
||||
```
|
||||
|
||||
### Other components
|
||||
|
||||
rust_gpiozero provides several components that are similar to GPIO Zero for controlling output and input devices. These include [LED][14], [Buzzer][15], [Motor][16], Pulse Width Modulation LED ([PWMLED][17]), [Servo][18], and [Button][19].
|
||||
|
||||
Support for other components, sensors, and devices will be added eventually. You can refer to the [documentation][20] for further usage information.
|
||||
|
||||
### More resources
|
||||
|
||||
rust_gpiozero is still a work in progress. If you need more resources for getting started with Rust on your Raspberry Pi, here are some useful links:
|
||||
|
||||
#### Raspberry Pi Peripheral Access Library (RPPAL)
|
||||
|
||||
Similar to GPIO Zero, which is based on the [RPi.GPIO][21] library, rust_gpiozero builds upon the awesome **[RPPAL][22]** library by [Rene van der Meer][23]. If you want more control for your projects using Rust, you should definitely try RPPAL. It has support for GPIO, Inter-Integrated Circuit (I 2C), hardware and software Pulse Width Modulation (PWM), and Serial Peripheral Interface (SPI). Universal asynchronous receiver-transmitter (UART) support is currently in development.
|
||||
|
||||
#### Sense HAT support
|
||||
|
||||
**[Sensehat-rs][24]** is a library by [Jonathan Pallant][25] ([@therealjpster][26]) that provides Rust support for the Raspberry Pi [Sense HAT][27] add-on board. Jonathan also has a [starter workshop][28] for using the library and he wrote a beginner's intro to use Rust on Raspberry Pi, "Read Sense HAT with Rust," in [Issue 73 of _The MagPi_][29] magazine.
|
||||
|
||||
### Wrap Up
|
||||
|
||||
Hopefully, this has inspired you to use the Rust programming language for physical computing on your Raspberry Pi. rust_gpiozero is a library which provides useful components such as LED, Buzzer, Motor, PWMLED, Servo, and Button. More features are planned and you can follow me on [twitter][30] or check out [my blog][31] to stay tuned.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/3/physical-computing-rust-raspberry-pi
|
||||
|
||||
作者:[Rahul Thakoor][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/rh_003784_02_os.comcareers_os_rh2x.png?itok=jbRfXinl
|
||||
[2]: https://projects.raspberrypi.org/en/projects/grandpa-scarer/4
|
||||
[3]: https://gpiozero.readthedocs.io/en/stable/#
|
||||
[4]: http://Opensource.com
|
||||
[5]: https://opensource.com/users/bennuttall
|
||||
[6]: https://crates.io/crates/rust_gpiozero
|
||||
[7]: https://www.rust-lang.org/
|
||||
[8]: https://www.rust-lang.org/learn
|
||||
[9]: https://doc.rust-lang.org/book/
|
||||
[10]: https://www.rust-lang.org/tools/install
|
||||
[11]: https://rustup.rs/
|
||||
[12]: https://github.com/rust-embedded/cross
|
||||
[13]: https://github.com/kunerd/clerk/wiki/How-to-use-HD44780-LCD-from-Rust#setting-up-the-cross-toolchain
|
||||
[14]: https://docs.rs/rust_gpiozero/0.2.0/rust_gpiozero/output_devices/struct.LED.html
|
||||
[15]: https://docs.rs/rust_gpiozero/0.2.0/rust_gpiozero/output_devices/struct.Buzzer.html
|
||||
[16]: https://docs.rs/rust_gpiozero/0.2.0/rust_gpiozero/output_devices/struct.Motor.html
|
||||
[17]: https://docs.rs/rust_gpiozero/0.2.0/rust_gpiozero/output_devices/struct.PWMLED.html
|
||||
[18]: https://docs.rs/rust_gpiozero/0.2.0/rust_gpiozero/output_devices/struct.Servo.html
|
||||
[19]: https://docs.rs/rust_gpiozero/0.2.0/rust_gpiozero/input_devices/struct.Button.html
|
||||
[20]: https://docs.rs/rust_gpiozero/
|
||||
[21]: https://pypi.org/project/RPi.GPIO/
|
||||
[22]: https://github.com/golemparts/rppal
|
||||
[23]: https://twitter.com/golemparts
|
||||
[24]: https://crates.io/crates/sensehat
|
||||
[25]: https://github.com/thejpster
|
||||
[26]: https://twitter.com/therealjpster
|
||||
[27]: https://www.raspberrypi.org/products/sense-hat/
|
||||
[28]: https://github.com/thejpster/pi-workshop-rs/
|
||||
[29]: https://www.raspberrypi.org/magpi/issues/73/
|
||||
[30]: https://twitter.com/rahulthakoor
|
||||
[31]: https://rahul-thakoor.github.io/
|
||||
@@ -1,94 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Linux Server Hardening Using Idempotency with Ansible: Part 1)
|
||||
[#]: via: (https://www.linux.com/blog/linux-server-hardening-using-idempotency-ansible-part-1)
|
||||
[#]: author: (Chris Binnie https://www.linux.com/users/chrisbinnie)
|
||||
|
||||
Linux Server Hardening Using Idempotency with Ansible: Part 1
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
[Creative Commons Zero][2]
|
||||
|
||||
I think it’s safe to say that the need to frequently update the packages on our machines has been firmly drilled into us. To ensure the use of latest features and also keep security bugs to a minimum, skilled engineers and even desktop users are well-versed in the need to update their software.
|
||||
|
||||
Hardware, software and SaaS (Software as a Service) vendors have also firmly embedded the word “firewall” into our vocabulary for both domestic and industrial uses to protect our computers. In my experience, however, even within potentially more sensitive commercial environments, few engineers actively tweak the operating system (OS) they’re working on, to any great extent at least, to bolster security.
|
||||
|
||||
Standard fare on Linux systems, for example, might mean looking at configuring a larger swap file to cope with your hungry application’s demands. Or, maybe adding a separate volume to your server for extra disk space, specifying a more performant CPU at launch time, installing a few of your favorite DevOps tools, or chucking a couple of certificates onto the filesystem for each new server you build. This isn’t quite the same thing.
|
||||
|
||||
### Improve your Security Posture
|
||||
|
||||
What I am specifically referring to is a mixture of compliance and security, I suppose. In short, there’s a surprisingly large number of areas in which a default OS can improve its security posture. We can agree that tweaking certain aspects of an OS are a little riskier than others. Consider your network stack, for example. Imagine that, completely out of the blue, your server’s networking suddenly does something unexpected and causes you troubleshooting headaches or even some downtime. This might happen because a new application or updated package suddenly expects routing to behave in a less-common way or needs a specific protocol enabled to function correctly.
|
||||
|
||||
However, there are many changes that you can make to your servers without suffering any sleepless nights. The version and flavor of an OS helps determine which changes and to what extent you might want to comfortably make. Most importantly though what’s good for the goose is rarely good for the gander. In other words every single server estate has different, both broad and subtle, requirements which makes each use case unique. And, don’t forget that a database server also has very different needs to a web server so you can have a number of differing needs even within one small cluster of servers.
|
||||
|
||||
Over the last few years I’ve introduced these hardening and compliance tweaks more than a handful of times across varying server estates in my DevSecOps roles. The OSs have included: Debian, Red Hat Enterprise Linux (RHEL) and their respective derivatives (including what I suspect will be the increasingly popular RHEL derivative, Amazon Linux). There have been times that, admittedly including a multitude of relatively tiny tweaks, the number of changes to a standard server build was into the hundreds. It all depended on the time permitted for the work, the appetite for any risks and the generic or specific nature of the OS tweaks.
|
||||
|
||||
In this article, we’ll discuss the theory around something called idempotency which, in hand with an automation tool such as Ansible, can provide the ongoing improvements to your server estate’s security posture. For good measure we’ll also look at a number of Ansible playbook examples and additionally refer to online resources so that you can introduce idempotency to a server estate near you.
|
||||
|
||||
### Say What?
|
||||
|
||||
In simple terms the word “idempotent” just means returning something back to how it was prior to a change. It can also mean that lots of things you wanted to be the same, for consistency, are exactly the same, too.
|
||||
|
||||
Picture that in action for a moment on a server estate; we’ll use AWS (Amazon Web Services) as our example. You create a new server image (Amazon Machine Images == AMIs) precisely how you want it with compliance and hardening introduced, custom packages, the removal of unwanted packages, SSH keys, user accounts etc and then spin up twenty servers using that AMI.
|
||||
|
||||
You know for certain that all the servers, at least at the time that they are launched, are absolutely identical. Trust me when I say that this is a “good thing” ™. The lack of what’s known as “config drift” means that if one package on a server needs updated for security reasons then all the servers need that package updated too. Or if there’s a typo in a config file that’s breaking an application then it affects all servers equally. There’s less administrative overhead, less security risk and greater levels of predictability in terms of achieving better uptime.
|
||||
|
||||
What about config drift from a security perspective? As you’ve guessed it’s definitely not welcome. That’s because engineers making manual changes to a “base OS build” can only lead to heartache and stress. The predictability of how a system is working suffers greatly as a result and servers running unique config become less reliable. These server systems are known as “snowflakes” as they’re unique but far less beautiful than actual snow.
|
||||
|
||||
Equally an attacker might have managed to breach one aspect, component or service on a server but not all of its facets. By rewriting our base config again and again we’re able to, with 100% certainty (if it’s set up correctly), predict exactly what a server will look like and therefore how it will perform. Using various tools you can also trigger alarms if changes are detected to request that a pair of human eyes have a look to see if it’s a serious issue and then adjust the base config if needed.
|
||||
|
||||
To make our machines idempotent we might overwrite our config changes every 20 or 30 minutes, for example. When it comes to running servers, that in essence, is what is meant by idempotency.
|
||||
|
||||
### Central Station
|
||||
|
||||
My mechanism of choice for repeatedly writing config across a large number of servers is running Ansible playbooks. It’s relatively easy to implement and removes the all-too-painful additional logic required when using shell scripts. Of the popular configuration management tools I’ve seen in action is Puppet, used successfully on a large government estate in an idempotent manner, but I prefer Ansible due to its more logical syntax (to my mind at least) and its readily available documentation.
|
||||
|
||||
Before we look at some simple Ansible examples of hardening an OS with idempotency in mind we should explore how to trigger our Ansible playbooks.
|
||||
|
||||
This is a larger area for debate than you might first imagine. Say, for example, you have nicely segmented server estate with production servers being carefully locked away from development servers, sitting behind a production-grade firewall. Consider the other servers on the estate, belonging to staging (pre-production) or other development environments, intentionally having different access permissions for security reasons.
|
||||
|
||||
If you’re going to run a centralized server that has superuser permissions (which are required to make privileged changes to your core system files) then that server will need to have high-level access permissions potentially across your entire server estate. It must therefore be guarded very closely.
|
||||
|
||||
You will also want to test your playbooks against development environments (in plural) to test their efficacy which means you’ll probably need two all-powerful centralised Ansible servers, one for production and one for the multiple development environments.
|
||||
|
||||
The actual approach of how to achieve other logistical issues is up for debate and I’ve heard it discussed a few times. Bear in mind that Ansible runs using plain, old SSH keys (a feature that something other configuration management tools have started to copy over time) but ideally you want a mechanism for keeping non-privileged keys on your centralised servers so you’re not logging in as the “root” user across the estate every twenty minutes or thirty minutes.
|
||||
|
||||
From a network perspective I like the idea of having firewalling in place to enforce one-way traffic only into the environment that you’re affecting. This protects your centralised host so that a compromised server can’t attack that main Ansible host easily and then as a result gain access to precious SSH keys in order to damage the whole estate.
|
||||
|
||||
Speaking of which, are servers actually needed for a task like this? What about using AWS Lambda (<https://aws.amazon.com/lambda>) to execute your playbooks? A serverless approach stills needs to be secured carefully but unquestionably helps to limit the attack surface and also potentially reduces administrative responsibilities.
|
||||
|
||||
I suspect how this all-powerful server is architected and deployed is always going to be contentious and there will never be a one-size-fits-all approach but instead a unique, bespoke solution will be required for every server estate.
|
||||
|
||||
### How Now, Brown Cow
|
||||
|
||||
It’s important to think about how often you run your Ansible and also how to prepare for your first execution of the playbook. Let’s get the frequency of execution out of the way first as it’s the easiest to change in the future.
|
||||
|
||||
My preference would be three times an hour or instead every thirty minutes. If we include enough detail in our configuration then our playbooks might prevent an attacker gaining a foothold on a system as the original configuration overwrites any altered config. Twenty minutes seems more appropriate to my mind.
|
||||
|
||||
Again, this is an aspect you need to have a think about. You might be dumping small config databases locally onto a filesystem every sixty minutes for example and that scheduled job might add an extra little bit of undesirable load to your server meaning you have to schedule around it.
|
||||
|
||||
Next time, we’ll take a look at some specific changes that can be made to various systems.
|
||||
|
||||
_Chris Binnie’s latest book, Linux Server Security: Hack and Defend, shows you how to make your servers invisible and perform a variety of attacks. You can find out more about DevSecOps, containers and Linux security on his website:[https://www.devsecops.cc][3]_
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/blog/linux-server-hardening-using-idempotency-ansible-part-1
|
||||
|
||||
作者:[Chris Binnie][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.linux.com/users/chrisbinnie
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.linux.com/sites/lcom/files/styles/rendered_file/public/geometric-1732847_1280.jpg?itok=YRux0Tua
|
||||
[2]: /LICENSES/CATEGORY/CREATIVE-COMMONS-ZERO
|
||||
[3]: https://www.devsecops.cc/
|
||||
@@ -1,207 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (topgrade – Upgrade/Update Everything In Single Command On Linux?)
|
||||
[#]: via: (https://www.2daygeek.com/topgrade-upgrade-update-everything-in-single-command-on-linux/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
topgrade – Upgrade/Update Everything In Single Command On Linux?
|
||||
======
|
||||
|
||||
As a Linux administrator, you have to keep your system up-to-date to get ride out from some unexpected issues.
|
||||
|
||||
We have to keep the system with latest patches as part of best practices.
|
||||
|
||||
To do so, you need to perform the patching activity at least once in a month.
|
||||
|
||||
Most of the time you have to reboot the server after patching to activate the latest kernel.
|
||||
|
||||
It’s good to reboot the server at least 90-120 days once that will fix some outstanding issue which we already having.
|
||||
|
||||
If you have a single system then we can directly login to the system and do perform the patching that is not a big deal.
|
||||
|
||||
Even, if you have few of servers with the same flavor then you can perform the patching with help of shell script.
|
||||
|
||||
If you have high number of servers then i would advise you to go with any of the parallel utility, which will help us to perform the patching in parallel.
|
||||
|
||||
It will save a lot’s of time compared with shell script as this go with sequential order.
|
||||
|
||||
how to patch all togeter if you have servers with multiple flavors? What will be the solution ?
|
||||
|
||||
I recently came to know the utility called `topgrade` that can fulfill your requirement.
|
||||
|
||||
Also, your distribution package manager doesn’t upgrade the packages which was installed with other package managers such as pip, npm, snap, etc,. but topgrade can fix this issue as well.
|
||||
|
||||
### What Is topgrade?
|
||||
|
||||
[topgrade][1] is a new tool that will upgrade all the installed packages on your system to latest available version by detecting and running the appropriate package managers.
|
||||
|
||||
### How To Install topgrade In Linux?
|
||||
|
||||
There is no separate package manager for distributions wise. Hence, you need to install topgrade with help of cargo package manager.
|
||||
|
||||
The topgrade is available in AUR. So, use one of the **[AUR helper][2]** to install it on Arch-based systems. I prefer to go with **[Yay helper][3]** program.
|
||||
|
||||
```
|
||||
$ yay -S topgrade
|
||||
```
|
||||
|
||||
Once you have installed the **[cargo package manager][4]** , use the following command to install it.
|
||||
|
||||
```
|
||||
$ cargo install topgrade
|
||||
```
|
||||
|
||||
Once topgrade is initiated, it will perform the following tasks one by one.
|
||||
|
||||
* Try to self-upgrade if any updated is available for topgrade.
|
||||
* Arch: Run yay or fall back to pacman
|
||||
* CentOS/RHEL: Run yum upgrade
|
||||
* Fedora: Run dnf upgrade
|
||||
* Debian/Ubuntu: Run apt update && apt dist-upgrade
|
||||
* openSUSE: Run zypper refresh && zypper dist-upgrade
|
||||
* Upgrade Vim/Neovim packages.
|
||||
* Run npm update -g if NPM is installed
|
||||
* Upgrade Atom packages
|
||||
* Linux: Update Flatpak packages
|
||||
* Linux: Update snap packages
|
||||
* Linux: Run fwupdmgr to show firmware upgrade.
|
||||
* Finally it will run needrestart to bounce all the services.
|
||||
|
||||
|
||||
|
||||
Now, we have successfully installed `topgrade` so, run the topgrade alone to upgrade everything on your system. I have tested the utility on Ubuntu 18.04 LTS and the results are below.
|
||||
|
||||
```
|
||||
$ topgrade
|
||||
|
||||
―― System update ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
[sudo] password for daygeek:
|
||||
Hit:1 http://in.archive.ubuntu.com/ubuntu bionic InRelease
|
||||
Get:2 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
|
||||
Get:3 http://in.archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB]
|
||||
Get:4 http://in.archive.ubuntu.com/ubuntu bionic-backports InRelease [74.6 kB]
|
||||
.
|
||||
Get:16 http://security.ubuntu.com/ubuntu bionic-security/universe DEP-11 64x64 Icons [45.2 kB]
|
||||
Get:17 http://security.ubuntu.com/ubuntu bionic-security/multiverse amd64 DEP-11 Metadata [2,460 B]
|
||||
Fetched 1,565 kB in 13s (117 kB/s)
|
||||
Reading package lists... Done
|
||||
Building dependency tree
|
||||
Reading state information... Done
|
||||
119 packages can be upgraded. Run 'apt list --upgradable' to see them.
|
||||
Reading package lists... Done
|
||||
Building dependency tree
|
||||
Reading state information... Done
|
||||
Calculating upgrade... Done
|
||||
The following packages were automatically installed and are no longer required:
|
||||
libopts25 linux-headers-4.15.0-45 linux-headers-4.15.0-45-generic linux-image-4.15.0-45-generic
|
||||
linux-modules-4.15.0-29-generic linux-modules-4.15.0-45-generic linux-modules-extra-4.15.0-45-generic sntp
|
||||
Use 'sudo apt autoremove' to remove them.
|
||||
The following packages will be upgraded:
|
||||
apport apport-gtk apt apt-utils cups cups-bsd cups-client cups-common cups-core-drivers cups-daemon cups-ipp-utils
|
||||
cups-ppdc cups-server-common distro-info-data fwupdate fwupdate-signed gir1.2-dbusmenu-glib-0.4 gir1.2-gtk-3.0
|
||||
gir1.2-packagekitglib-1.0 gir1.2-snapd-1 gnome-settings-daemon gnome-settings-daemon-schemas grub-common grub-pc
|
||||
python3-httplib2 python3-problem-report samba-libs systemd systemd-sysv ubuntu-drivers-common udev ufw
|
||||
unattended-upgrades xdg-desktop-portal xdg-desktop-portal-gtk
|
||||
119 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
|
||||
Need to get 38.5 MB of archives.
|
||||
After this operation, 475 kB of additional disk space will be used.
|
||||
Do you want to continue? [Y/n]
|
||||
.
|
||||
.
|
||||
Setting up grub-pc (2.02-2ubuntu8.13) ...
|
||||
Installing for i386-pc platform.
|
||||
Installation finished. No error reported.
|
||||
Sourcing file `/etc/default/grub'
|
||||
Generating grub configuration file ...
|
||||
Found memtest86+ image: /boot/memtest86+.elf
|
||||
Found memtest86+ image: /boot/memtest86+.bin
|
||||
done
|
||||
Setting up mesa-vdpau-drivers:amd64 (18.2.8-0ubuntu0~18.04.2) ...
|
||||
Updating PPD files for cups ...
|
||||
Setting up apport-gtk (2.20.9-0ubuntu7.6) ...
|
||||
Setting up pulseaudio-module-bluetooth (1:11.1-1ubuntu7.2) ...
|
||||
Processing triggers for libc-bin (2.27-3ubuntu1) ...
|
||||
Processing triggers for initramfs-tools (0.130ubuntu3.7) ...
|
||||
update-initramfs: Generating /boot/initrd.img-4.15.0-47-generic
|
||||
```
|
||||
|
||||
It will run the self-updates once the distribution official packages update done.
|
||||
|
||||
```
|
||||
―― rustup ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
info: checking for self-updates
|
||||
info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu'
|
||||
info: checking for self-updates
|
||||
|
||||
stable-x86_64-unknown-linux-gnu unchanged - rustc 1.33.0 (2aa4c46cf 2019-02-28)
|
||||
```
|
||||
|
||||
Then it will try to update the packages that has installed with other package managers.
|
||||
|
||||
```
|
||||
―― Flatpak User Packages ――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
Looking for updates...
|
||||
Looking for updates...
|
||||
Updating in system:
|
||||
org.gnome.Platform/x86_64/3.30 flathub 862e6b8ec2b5
|
||||
org.gnome.Platform.Locale/x86_64/3.30 flathub 5e66e981ae00
|
||||
org.freedesktop.Platform.html5-codecs/x86_64/18.08 flathub 282fd2c4ef33
|
||||
com.github.muriloventuroso.easyssh/x86_64/stable flathub c6bc3a3e72fb
|
||||
new permissions: ssh-auth
|
||||
com.github.muriloventuroso.easyssh.Locale/x86_64/stable flathub b705864b8d78
|
||||
Updating: org.gnome.Platform/x86_64/3.30 from flathub
|
||||
[####################] 16 delta parts, 10 loose fetched; 65539 KiB transferred in 63 seconds
|
||||
Error: Failed to update org.gnome.Platform/x86_64/3.30: Flatpak system operation Deploy not allowed for user
|
||||
|
||||
Skipping org.gnome.Platform.Locale/x86_64/3.30 due to previous error
|
||||
|
||||
Skipping org.freedesktop.Platform.html5-codecs/x86_64/18.08 due to previous error
|
||||
Updating: com.github.muriloventuroso.easyssh/x86_64/stable from flathub
|
||||
[####################] 2 delta parts, 3 loose fetched; 1532 KiB transferred in 5 seconds
|
||||
Error: Failed to update com.github.muriloventuroso.easyssh/x86_64/stable: Flatpak system operation Deploy not allowed for user
|
||||
|
||||
Skipping com.github.muriloventuroso.easyssh.Locale/x86_64/stable due to previous error
|
||||
error: There were one or more errors
|
||||
|
||||
Retry? [y/N]
|
||||
```
|
||||
|
||||
Then it will run the firmwre upgrade.
|
||||
|
||||
```
|
||||
―― Firmware upgrades ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
Fetching metadata https://cdn.fwupd.org/downloads/firmware.xml.gz
|
||||
Downloading… [***************************************]
|
||||
Fetching signature https://cdn.fwupd.org/downloads/firmware.xml.gz.asc
|
||||
```
|
||||
|
||||
Finally, it shows the summary about the patching has done.
|
||||
|
||||
```
|
||||
―― Summary ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
System update: OK
|
||||
rustup: OK
|
||||
Flatpak User Packages: FAILED
|
||||
Firmware upgrade: OK
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/topgrade-upgrade-update-everything-in-single-command-on-linux/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://github.com/r-darwish/topgrade
|
||||
[2]: https://www.2daygeek.com/category/aur-helper/
|
||||
[3]: https://www.2daygeek.com/install-yay-yet-another-yogurt-aur-helper-on-arch-linux/
|
||||
[4]: https://www.2daygeek.com/how-to-install-rust-programming-language-in-linux/
|
||||
@@ -1,116 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Linux Server Hardening Using Idempotency with Ansible: Part 2)
|
||||
[#]: via: (https://www.linux.com/blog/linux-server-hardening-using-idempotency-ansible-part-2)
|
||||
[#]: author: (Chris Binnie https://www.linux.com/users/chrisbinnie)
|
||||
|
||||
Linux Server Hardening Using Idempotency with Ansible: Part 2
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
[Creative Commons Zero][2]
|
||||
|
||||
In the first part of this series, we introduced something called idempotency, which can provide the ongoing improvements to your server estate’s security posture. In this article, we’ll get a little more hands-on with a look at some specific Ansible examples.
|
||||
|
||||
### Shopping List
|
||||
|
||||
You will need some Ansible experience before being able to make use of the information that follows. Rather than run through the installation and operation of Ansible let’s instead look at some of the idempotency playbook’s content.
|
||||
|
||||
As mentioned earlier there might be hundreds of individual system tweaks to make on just one type of host so we’ll only explore a few suggested Ansible tasks and how I like to structure the Ansible role responsible for the compliance and hardening. You have hopefully picked up on the fact that the devil is in the detail and you should absolutely, unequivocally, understand to as high a level of detail as possible, about the permutations of making changes to your server OS.
|
||||
|
||||
Be aware that I will mix and match between OSs in the Ansible examples that follow. Many examples are OS agnostic but as ever you should pay close attention to the detail. Obvious changes like “apt” to “yum” for the package manager is a given.
|
||||
|
||||
Inside a “tasks” file under our Ansible “hardening” role, or whatever you decide to name it, these named tasks represent the areas of a system with some example code to offer food for thought. In other words, each section that follows will probably be a single YAML file, such as “accounts.yml”, and each will have with varying lengths and complexity.
|
||||
|
||||
Let’s look at some examples with ideas about what should go into each file to get you started. The contents of each file that follow are just the very beginning of a checklist and the following suggestions are far from exhaustive.
|
||||
|
||||
#### SSH Server
|
||||
|
||||
This is the application that almost all engineers immediately look to harden when asked to secure a server. It makes sense as SSH (the OpenSSH package in many cases) is usually only one of a few ports intentionally prised open and of course allows direct access to the command line. The level of hardening that you should adopt is debatable. I believe in tightening the daemon as much as possible without disruption and would usually make around fifteen changes to the standard OpenSSH server config file, “sshd_config”. These changes would include pulling in a MOTD banner (Message Of The Day) for legal compliance (warning of unauthorised access and prosecution), enforcing the permissions on the main SSHD files (so they can’t be tampered with by lesser-privileged users), ensuring the “root” user can’t log in directly, setting an idle session timeout and so on.
|
||||
|
||||
Here’s a very simple Ansible example that you can repeat within other YAML files later on, focusing on enforcing file permissions on our main, critical OpenSSH server config file. Note that you should carefully check every single file that you hard-reset permissions on before doing so. This is because there are horrifyingly subtle differences between Linux distributions. Believe me when I say that it’s worth checking first.
|
||||
|
||||
name: Hard reset permissions on sshd server file
|
||||
|
||||
file: owner=root group=root mode=0600 path=/etc/ssh/sshd_config
|
||||
|
||||
To check existing file permissions I prefer this natty little command for the job:
|
||||
|
||||
```
|
||||
$ stat -c "%a %n" /etc/ssh/sshd_config
|
||||
|
||||
644 /etc/ssh/sshd_config
|
||||
```
|
||||
|
||||
As our “stat” command shows our Ansible snippet would be an improvement to the current permissions because 0600 means only the “root” user can read and write to that file. Other users or groups can’t even read that file which is of benefit because if we’ve made any mistakes in securing SSH’s config they can’t be discovered as easily by less-privileged users.
|
||||
|
||||
#### System Accounts
|
||||
|
||||
At a simple level this file might define how many users should be on a standard server. Usually a number of users who are admins have home directories with public keys copied into them. However this file might also include performing simple checks that the root user is the only system user with the all-powerful superuser UID 0; in case an attacker has altered user accounts on the system for example.
|
||||
|
||||
#### Kernel
|
||||
|
||||
Here’s a file that can grow arms and legs. Typically I might affect between fifteen and twenty sysctl changes on an OS which I’m satisfied won’t be disruptive to current and, all going well, any future uses of a system. These changes are again at your discretion and, at my last count (as there’s between five hundred and a thousand configurable kernel options using sysctl on a Debian/Ubuntu box) you might opt to split off these many changes up into different categories.
|
||||
|
||||
Such categories might include network stack tuning, stopping core dumps from filling up disk space, disabling IPv6 entirely and so on. Here’s an Ansible example of logging network packets that shouldn’t been routed out onto the Internet, namely those packets using spoofed private IP Addresses, called “martians”.
|
||||
|
||||
name: Keep track of traffic that shouldn’t be routed onto the Internet
|
||||
|
||||
lineinfile: dest="/etc/sysctl.conf" line="{{item.network}}" state=present
|
||||
|
||||
with_items:
|
||||
|
||||
\- { network: 'net.ipv4.conf.all.log_martians = 1' }
|
||||
|
||||
\- { network: 'net.ipv4.conf.default.log_martians = 1' }
|
||||
|
||||
Pay close attention that you probably don’t want to use the file “/etc/sysctl.conf” but create a custom file under the directory “/etc/sysctl.d/” or similar. Again, check your OS’s preference, usually in the comments of the pertinent files. If you’ve not seen martian packets being enabled before then type “dmesg” (sometimes only as the “root” user) to view kernel messages and after a week or two of logging being in place you’ll probably see some traffic polluting your logs. It’s much better to know how attackers are probing your servers than not. A few log entries for reference can only be of value. When it comes to looking after servers, ignorance is certainly not bliss.
|
||||
|
||||
#### Network
|
||||
|
||||
As mentioned you might want to include hardening the network stack within your kernel.yml file, depending on whether there’s many entries or not, or simply for greater clarity. For your network.yml file have a think about stopping old-school broadcast attacks flooding your LAN and ICMP oddities from changing your routing in addition.
|
||||
|
||||
#### Services
|
||||
|
||||
Usually I would stop or start miscellaneous system services (and potentially applications) within this Ansible file. If there weren’t many services then rather than also using a “cron.yml” file specifically for “cron” hardening I’d include those here too.
|
||||
|
||||
There’s a bundle of changes you can make around cron’s file permissions etc. If you haven’t come across it, on some OSs, there’s a “cron.deny” file for example which blacklists certain users from accessing the “crontab” command. Additionally you also have a multitude of cron directories under the “/etc” directory which need permissions enforced and improved, indeed along with the file “/etc/crontab” itself. Once again check with your OS’s current settings before altering these or “bad things” ™ might happen to your uptime.
|
||||
|
||||
In terms of miscellaneous services being purposefully stopped and certain services, such as system logging which is imperative to a healthy and secure system, have a quick look at the Ansible below which I might put in place for syslog as an example.
|
||||
|
||||
name: Insist syslog is definitely installed (so we can receive upstream logs)
|
||||
|
||||
apt: name=rsyslog state=present
|
||||
|
||||
name: Make sure that syslog starts after a reboot
|
||||
|
||||
service: name=rsyslog state=started enabled=yes
|
||||
|
||||
#### IPtables
|
||||
|
||||
The venerable Netfilter which, from within the Linux kernel offers the IPtables software firewall the ability to filter network packets in an exceptionally sophisticated manner, is a must if you can enable it sensibly. If you’re confident that each of your varying flavours of servers (whether it’s a webserver, database server and so on) can use the same IPtables config then copy a file onto the filesystem via Ansible and make sure it’s always loaded up using this YAML file.
|
||||
|
||||
Next time, we’ll wrap up our look at specific system suggestions and talk a little more about how the playbook might be used.
|
||||
|
||||
Chris Binnie’s latest book, Linux Server Security: Hack and Defend, shows you how to make your servers invisible and perform a variety of attacks. You can find out more about DevSecOps, containers and Linux security on his website: [https://www.devsecops.cc][3]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/blog/linux-server-hardening-using-idempotency-ansible-part-2
|
||||
|
||||
作者:[Chris Binnie][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.linux.com/users/chrisbinnie
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.linux.com/sites/lcom/files/styles/rendered_file/public/artificial-intelligence-3382507_1280.jpg?itok=PHazitpd
|
||||
[2]: /LICENSES/CATEGORY/CREATIVE-COMMONS-ZERO
|
||||
[3]: https://www.devsecops.cc/
|
||||
@@ -1,118 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Linux Server Hardening Using Idempotency with Ansible: Part 3)
|
||||
[#]: via: (https://www.linux.com/blog/linux-server-hardening-using-idempotency-ansible-part-3)
|
||||
[#]: author: (Chris Binnie https://www.linux.com/users/chrisbinnie)
|
||||
|
||||
Linux Server Hardening Using Idempotency with Ansible: Part 3
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
[Creative Commons Zero][2]
|
||||
|
||||
In the previous articles, we introduced idempotency as a way to approach your server’s security posture and looked at some specific Ansible examples, including the kernel, system accounts, and IPtables. In this final article of the series, we’ll look at a few more server-hardening examples and talk a little more about how the idempotency playbook might be used.
|
||||
|
||||
#### **Time**
|
||||
|
||||
Due to its reduced functionality, and therefore attack surface, the preference amongst a number of OSs has been to introduce “chronyd” over “ntpd”. If you’re new to “chrony” then fret not. It’s still using the NTP (Network Time Protocol) that we all know and love but in a more secure fashion.
|
||||
|
||||
The first thing I do with Ansible within the “chrony.conf” file is alter the “bind address” and if my memory serves there’s also a “command port” option. These config options allow Chrony to only listen on the localhost. In other words you are still syncing as usual with other upstream time servers (just as NTP does) but no remote servers can query your time services; only your local machine has access.
|
||||
|
||||
There’s more information on the “bindcmdaddress 127.0.0.1” and “cmdport 0” on this Chrony page (<https://chrony.tuxfamily.org/faq.html>) under “2.5. How can I make chronyd more secure?” which you should read for clarity. This premise behind the comment on that page is a good idea: “you can disable the internet command sockets completely by adding cmdport 0 to the configuration file”.
|
||||
|
||||
Additionally I would also focus on securing the file permissions for Chrony and insist that the service starts as expected just like the syslog config above. Otherwise make sure that your time sources are sane, have a degree of redundancy with multiple sources set up and then copy the whole config file over using Ansible.
|
||||
|
||||
#### **Logging**
|
||||
|
||||
You can clearly affect the level of detail included in the logs from a number pieces of software on a server. Thinking back to what we’ve looked at in relation to syslog already you can also tweak that application’s config using Ansible to your needs and then use the example Ansible above in addition.
|
||||
|
||||
#### **PAM**
|
||||
|
||||
Apparently PAM (Pluggable Authentication Modules) has been a part of Linux since 1997. It is undeniably useful (a common use is that you can force SSH to use it for password logins, as per the SSH YAML file above). It is extensible, sophisticated and can perform useful functions such as preventing brute force attacks on password logins using a clever rate limiting system. The syntax varies a little between OSes but if you have the time then getting PAM working well (even if you’re only using SSH keys and not passwords for your logins) is a worthwhile effort. Attackers like their own users on a system with lots of usernames, something innocuous such as “webadmin” or similar might be easy to miss on a server, and PAM can help you out in this respect.
|
||||
|
||||
#### **Auditd**
|
||||
|
||||
We’ve looked at logging a little already but what about capturing every “system call” that a kernel makes. The Linux kernel is a super-busy component of any system and logging almost every single thing that a system does is an excellent way of providing post-event forensics. This article will hopefully shed some light on where to begin: <http://www.admin-magazine.com/Archive/2018/43/Auditing-Docker-Containers-in-a-DevOps-Environment>. Note the comments in that article about performance, there’s little point in paying extra for compute and disk IO resource because you’ve misconfigured your logging so spend some time getting it correct would be my advice.
|
||||
|
||||
For concerns over disk space I will usually change a few lines in the file “/etc/audit/auditd.conf” in order to prevent there firstly being too many log files created and secondly logs that grow very large without being rotated. This is also on the proviso that logs are being ingested upstream via another mechanism too. Clearly the files permissions and the service starting are also the basics you need to cover here too. Generally file permissions for auditd are tight as it’s a “root” oriented service so there’s less changes needed here generally.
|
||||
|
||||
#### **Filesystems**
|
||||
|
||||
With a little reading you can discover which filesystems that are made available to your OS by default. You should disable these (at the “modprode.d” file level) with Ansible to prevent weird and wonderful things being attached unwittingly to your servers. You are reducing the attack surface with this approach. The Ansible might look something like this below for example.
|
||||
|
||||
```
|
||||
name: Make sure filesystems which are not needed are forced as off
|
||||
|
||||
lineinfile: dest="/etcmodprobe.d/harden.conf" line='install squashfs /bin/true' state=present
|
||||
```
|
||||
|
||||
#### **SELinux**
|
||||
|
||||
The old, but sometimes avoided due to complexity, security favourite, SELinux, should be set to “enforcing” mode. Or, at the every least, set to log sensibly using “permissive” mode. Permissive mode will at least fill your auditd logs up with any correct rule matches nicely. In terms of what Ansible looks like it’s simple and is along these lines:
|
||||
|
||||
```
|
||||
name: Configure SElinux to be running in permissive mode
|
||||
|
||||
replace: path=”/etc/selinux/config” regexp='SELINUX=disabled' replace='SELINUX=permissive'
|
||||
```
|
||||
|
||||
#### **Packages**
|
||||
|
||||
Needless to say the compliance hardening playbook is also a good place to upgrade all the packages (with some selective exclusions) on the system. Pay attention to the section relating to reboots and idempotency in a moment however. With other mechanisms in place you might not want to update packages here but instead as per the Automation Documents article mentioned in a moment.
|
||||
|
||||
### **Idempotency**
|
||||
|
||||
Now we’ve run through some of the aspects you would want to look at when hardening on a server, let’s think a little more about how the playbook might be used.
|
||||
|
||||
When it comes to cloud platforms most of my professional work has been on AWS and therefore, more often than not, a fresh AMI is launched and then a playbook is run over the top of it. There’s a mountain of detail in one way of doing that in this article (<http://www.admin-magazine.com/Archive/2018/45/AWS-Automation-Documents>) which you may be pleased to discover accommodates a mechanism to spawn a script or playbook.
|
||||
|
||||
It is important to note, when it comes to idempotency, that it may take a little more effort initially to get your head around the logic involved in being able to re-run Ansible repeatedly without disturbing the required status quo of your server estate.
|
||||
|
||||
One thing to be absolutely certain of however (barring rare edge cases) is that after you apply your hardening for the very first time, on a new AMI or server build, you will require a reboot. This is an important element due to a number of system facets not being altered correctly without a reboot. These include applying kernel changes so alterations become live, writing auditd rules as immutable config and also starting or stopping services to improve the security posture.
|
||||
|
||||
Note though that you’re probably not going to want to execute all plays in a playbook every twenty or thirty minutes, such as updating all packages and stopping and restarting key customer-facing services. As a result you should factor the logic into your Ansible so that some tasks only run once initially and then maybe write a “completed” placeholder file to the filesystem afterwards for referencing. There’s a million different ways of achieving a status checker.
|
||||
|
||||
The nice thing about Ansible is that the logic for rerunning playbooks is implicit and unlike shell scripts which for this type of task can be arduous to code the logic into. Sometimes, such as updating the GRUB bootloader for example, trying to guess the many permutations of a system change can be painful.
|
||||
|
||||
### **Bedtime Reading**
|
||||
|
||||
I still think that you can’t beat trial and error when it comes to computing. Experience is valued for good reason.
|
||||
|
||||
Be warned that you’ll find contradictory advice sometimes from the vast array of online resources in this area. Advice differs probably because of the different use cases. The only way to harden the varying flavours of OS to my mind is via a bespoke approach. This is thanks to the environments that servers are used within and the requirements of the security framework or standard that an organisation needs to meet.
|
||||
|
||||
For OS hardening details you can check with resources such as the NSA ([https://www.nsa.gov][3]), the Cloud Security Alliance (<https://cloudsecurityalliance.org/working-groups/security-guidance/#_overview>), proprietary training organisations such as GIAC ([https://www.giac.org][4]) who offer resources (<https://www.giac.org/paper/gcux/97/red-hat-linux-71-installation-hardening-checklist/102167>), the diverse CIS Benchmarks ([https://www.cisecurity.org][5]) for industry consensus-based benchmarking, the SANS Institute (<https://uk.sans.org/score/checklists>), NIST’s Computer Security Research ([https://csrc.nist.gov][6]) and of course print media too.
|
||||
|
||||
### **Conclusion**
|
||||
|
||||
Hopefully, you can see how powerful an idempotent server infrastructure is and are tempted to try it for yourself.
|
||||
|
||||
The ever-present threat of APT (Advanced Persistent Threat) attacks on infrastructure, where a successful attacker will sit silently monitoring events and then when it’s opportune infiltrate deeper into an estate, makes this type of configuration highly valuable.
|
||||
|
||||
The amount of detail that goes into the tests and configuration changes is key to the value that such an approach will bring to an organisation. Like the tests in a CI/CD pipeline they’re only as ever as good as their coverage.
|
||||
|
||||
Chris Binnie’s latest book, Linux Server Security: Hack and Defend, shows you how to make your servers invisible and perform a variety of attacks. You can find out more about DevSecOps, containers and Linux security on his website: [https://www.devsecops.cc][7]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/blog/linux-server-hardening-using-idempotency-ansible-part-3
|
||||
|
||||
作者:[Chris Binnie][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.linux.com/users/chrisbinnie
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.linux.com/sites/lcom/files/styles/rendered_file/public/tech-1495181_1280.jpg?itok=5WcwApNN
|
||||
[2]: /LICENSES/CATEGORY/CREATIVE-COMMONS-ZERO
|
||||
[3]: https://www.nsa.gov/
|
||||
[4]: https://www.giac.org/
|
||||
[5]: https://www.cisecurity.org/
|
||||
[6]: https://csrc.nist.gov/
|
||||
[7]: https://www.devsecops.cc/
|
||||
@@ -1,223 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Hardware bootstrapping with Ansible)
|
||||
[#]: via: (https://opensource.com/article/19/5/hardware-bootstrapping-ansible)
|
||||
[#]: author: (Mark Phillips https://opensource.com/users/markp/users/feeble/users/markp)
|
||||
|
||||
Hardware bootstrapping with Ansible
|
||||
======
|
||||
|
||||
![computer servers processing data][1]
|
||||
|
||||
At a recent [Ansible London Meetup][2], I got chatting with somebody about automated hardware builds. _"It's all cloud now!"_ I hear you say. Ah, but for many large organisations it's not—they still have massive data centres full of hardware. Almost regularly somebody pops up on our internal mail list and asks, *"can Ansible do hardware provisioning?" *Well yes, you can provision hardware with Ansible…
|
||||
|
||||
### Requirements
|
||||
|
||||
Bootstrapping hardware is mostly about network services. Before we do any operating system (OS) installing then, we must set up some services. We will need:
|
||||
|
||||
* DHCP
|
||||
* PXE
|
||||
* TFTP
|
||||
* Operating system media
|
||||
* Web server
|
||||
|
||||
|
||||
|
||||
### Setup
|
||||
|
||||
Besides the DHCP configuration, everything else in this article is handled by the Ansible plays included in [this repository][3].
|
||||
|
||||
#### DHCP server
|
||||
|
||||
I'm writing here on the assumption you can control your DHCP configuration. If you don't have access to your DHCP server, you'll need to ask the owner to set two options. DHCP option 67 needs to be set to **pxelinux.0** and **next-server** (which is option 66—but you may not need to know that; often a DHCP server will have a field/option for 'next server') needs to be set to the IP address of your TFTP server.
|
||||
|
||||
If you can own the DHCP server, I'd suggest using dnsmasq. It's small and simple. I will not cover configuring it here, but look at [the man page][4] and the **\--enable-tftp** option.
|
||||
|
||||
#### TFTP
|
||||
|
||||
The **next-server** setting for our DHCP server, above, will point to a machine serving [TFTP][5]. Here I've used a [CentOS Linux][6] virtual machine, as it only takes one package (syslinux-tftpboot) and a service to start to have TFTP up and running. We'll stick with the default path, **/var/lib/tftpboot**.
|
||||
|
||||
#### PXE
|
||||
|
||||
If you're not already familiar with PXE, you might like to take a quick look at [the Wikipedia page][7]. For this article I'll keep it short—we will serve some files over TFTP, which DHCP guides our hardware to.
|
||||
|
||||
You'll want **images/pxeboot/{initrd.img,vmlinuz}** from the OS distribution media for pxeboot. These need to be copied to **/var/lib/tftpboot/pxeboot**. The referenced Ansible plays **do not do this step, **so you need to copy them over yourself.
|
||||
|
||||
We'll also need to serve the OS installation files. There are two approaches to this: 1) install, via HTTP, from the internet or 2) install, again via HTTP, from a local server. For my testing, since I'm on a private LAN (and I guess you are too), the fastest installation method is the second. The easiest way to prepare this is to mount the DVD image and rsync the `images`, **`Packages` **and `repodata` directories to your webserver location. The referenced Ansible plays will install **httpd** but won't copy over these files, so don't forget to do that after running [the play][8]. For this article, we'll once again stick with defaults for simplicity—so files need to be copied to Apache's standard docroot, **/var/www/html**.
|
||||
|
||||
#### Directories
|
||||
|
||||
We should end up with directory structures like this:
|
||||
|
||||
##### PXE/TFTP
|
||||
|
||||
|
||||
```
|
||||
[root@c7 ~]# tree /var/lib/tftpboot/pxe{b*,l*cfg}
|
||||
/var/lib/tftpboot/pxeboot
|
||||
└── 6
|
||||
├── initrd.img
|
||||
└── vmlinuz
|
||||
```
|
||||
|
||||
##### httpd
|
||||
|
||||
|
||||
```
|
||||
[root@c7 ~]# tree -d /var/www/html/
|
||||
/var/www/html/
|
||||
├── 6 -> centos/6
|
||||
├── 7 -> centos/7
|
||||
├── centos
|
||||
│ ├── 6
|
||||
│ │ └── os
|
||||
│ │ └── x86_64
|
||||
│ │ ├── images
|
||||
│ │ │ └── pxeboot
|
||||
│ │ ├── Packages
|
||||
│ │ └── repodata
|
||||
│ └── 7
|
||||
│ └── os
|
||||
│ └── x86_64
|
||||
│ ├── images
|
||||
│ │ └── pxeboot
|
||||
│ ├── Packages
|
||||
│ └── repodata
|
||||
└── ks
|
||||
```
|
||||
|
||||
You'll notice my web setup appears a little less simple than the words above! I've pasted my actual structure to give you some ideas. The hardware I'm using is really old, and even getting CentOS 7 to work was horrible (if you're interested, it's due to the lack of [cciss][9] drivers for the HP Smart Array controller—yes, [there is an answer][10], but it takes a lot of faffing to make work), so all examples are of CentOS 6. I also wanted a flexible setup that could install many versions. Here I've done that using symlinks—this arrangement will work just fine for RHEL too, for example. The basic structure is present though—note the images, Packages and repodata directories.
|
||||
|
||||
These paths relate directly to [the PXE menu][11] file we'll serve up and [the kickstart file][12] too.
|
||||
|
||||
#### If you don't have DHCP
|
||||
|
||||
If you can't manage your own DHCP server or the owners of your infrastructure can't help, there is another option. In the past, I've used [iPXE][13] to create a boot image that I've loaded as virtual media. A lot of out-of-band/lights-out-management (LOM) interfaces on modern hardware support this functionality. You can make a custom embedded PXE menu in seconds with iPXE. I won't cover that here, but if it turns out to be a problem for you, then drop me a line [on Twitter][14] and I'll look at doing a follow-up blog post if enough people request it.
|
||||
|
||||
### Installing hardware
|
||||
|
||||
We've got our structure in place now, and we can [kickstart][15] a server. Before we do, we have to add some configuration to the TFTP setup to enable a given piece of hardware to pick up the PXE boot menu.
|
||||
|
||||
It's here we come across a small chicken/egg problem. We need a host's MAC address to create a link to the specific piece of hardware we want to kickstart. If the hardware is already running and we can access it with Ansible, that's great—we have a way of finding out the boot interface MAC address via the setup module (see [the reinstall play][16]). If it's a new piece of tin, however, we need to get the MAC address and tell our setup what to do with it. This probably means some manual intervention—booting the server and looking at a screen or maybe getting the MAC from a manifest or such like. Whichever way you get hold of it, we can tell our play about it via the inventory.
|
||||
|
||||
Let's put a custom variable into our simple INI format [inventory file][17], but run a play to set up TFTP…
|
||||
|
||||
|
||||
```
|
||||
(pip)iMac:ansible-hw-bootstrap$ ansible-inventory --host hp.box
|
||||
{
|
||||
"ilo_ip": "192.168.1.68",
|
||||
"ilo_password": "administrator"
|
||||
}
|
||||
(pip)iMac:ansible-hw-bootstrap$ ansible-playbook plays/install.yml
|
||||
|
||||
PLAY [kickstart] *******************************************************************************************************
|
||||
|
||||
TASK [Host inventory entry has a MAC address] **************************************************************************
|
||||
failed: [ks.box] (item=hp.box) => {
|
||||
"assertion": "hostvars[item]['mac'] is defined",
|
||||
"changed": false,
|
||||
"evaluated_to": false,
|
||||
"item": "hp.box",
|
||||
"msg": "Assertion failed"
|
||||
}
|
||||
|
||||
PLAY RECAP *************************************************************************************************************
|
||||
ks.box : ok=0 changed=0 unreachable=0 failed=1
|
||||
```
|
||||
|
||||
Uh oh, play failed. It [contains a check][18] that the host we're about to install actually has a MAC address added. Let's fix that and run the play again…
|
||||
|
||||
|
||||
```
|
||||
(pip)iMac:ansible-hw-bootstrap$ ansible-inventory --host hp.box
|
||||
{
|
||||
"ilo_ip": "192.168.1.68",
|
||||
"ilo_password": "administrator",
|
||||
"mac": "00:AA:BB:CC:DD:EE"
|
||||
}
|
||||
(pip)iMac:ansible-hw-bootstrap$ ansible-playbook plays/install.yml
|
||||
|
||||
PLAY [kickstart] *******************************************************************************************************
|
||||
|
||||
TASK [Host inventory entry has a MAC address] **************************************************************************
|
||||
ok: [ks.box] => (item=hp.box) => {
|
||||
"changed": false,
|
||||
"item": "hp.box",
|
||||
"msg": "All assertions passed"
|
||||
}
|
||||
|
||||
TASK [Set PXE menu to install] *****************************************************************************************
|
||||
ok: [ks.box] => (item=hp.box)
|
||||
|
||||
TASK [Reboot target host for PXE boot] *********************************************************************************
|
||||
skipping: [ks.box] => (item=hp.box)
|
||||
|
||||
PLAY RECAP *************************************************************************************************************
|
||||
ks.box : ok=2 changed=0 unreachable=0 failed=0
|
||||
```
|
||||
|
||||
That worked! What did it do? Looking at the pxelinux.cfg directory under our TFTP root, we can see a symlink…
|
||||
|
||||
|
||||
```
|
||||
[root@c7 pxelinux.cfg]# pwd
|
||||
/var/lib/tftpboot/pxelinux.cfg
|
||||
[root@c7 pxelinux.cfg]# l
|
||||
total 12
|
||||
drwxr-xr-x. 2 root root 65 May 13 14:23 ./
|
||||
drwxr-xr-x. 4 root root 4096 May 2 22:13 ../
|
||||
-r--r--r--. 1 root root 515 May 2 12:22 00README
|
||||
lrwxrwxrwx. 1 root root 7 May 13 14:12 01-00-aa-bb-cc-dd-ee -> install
|
||||
-rw-r--r--. 1 root root 682 May 2 22:07 install
|
||||
```
|
||||
|
||||
The **install** file is symlinked to a file named after our MAC address. This is the key, useful piece. It will ensure our hardware with MAC address **00-aa-bb-cc-dd-ee** is served a PXE menu when it boots from its network card.
|
||||
|
||||
So let's boot our machine.
|
||||
|
||||
Usefully, Ansible has some [remote management modules][19]. We're working with an HP server here, so we can use the [hpilo_boot][20] module to save us from having to interact directly with the LOM web interface.
|
||||
|
||||
Let's run the reinstall play on a booted server…
|
||||
|
||||
The neat thing about the **hpilo_boot** module, you'll notice, is it sets the boot medium to be the network. When the installation completes, the server restarts and boots from its hard drive. The eagle-eyed amongst you will have spotted the critical problem with this—what happens if the server boots to its network card again? It will pick up the PXE menu and promptly reinstall itself. I would suggest removing the symlink as a "belt and braces" step then. I will leave that as an exercise for you, dear reader. Hint: I would make the new server do a 'phone home' on boot, to somewhere, which runs a clean-up job. Since you wouldn't need the console open, as I had here to demonstrate what's going on in the background, a 'phone home' job would also give a nice indication that the process completed. Ansible, [naturally][21]. Good luck!
|
||||
|
||||
If you've any thoughts or comments on this process, please let me know.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/hardware-bootstrapping-ansible
|
||||
|
||||
作者:[Mark Phillips][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/markp/users/feeble/users/markp
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/server_data_system_admin.png?itok=q6HCfNQ8 (computer servers processing data)
|
||||
[2]: https://www.meetup.com/Ansible-London/
|
||||
[3]: https://github.com/phips/ansible-hw-bootstrap
|
||||
[4]: http://www.thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html
|
||||
[5]: https://en.m.wikipedia.org/wiki/Trivial_File_Transfer_Protocol
|
||||
[6]: https://www.centos.org
|
||||
[7]: https://en.m.wikipedia.org/wiki/Preboot_Execution_Environment
|
||||
[8]: https://github.com/phips/ansible-hw-bootstrap/blob/master/plays/kickstart.yml
|
||||
[9]: https://linux.die.net/man/4/cciss
|
||||
[10]: https://serverfault.com/questions/611182/centos-7-x64-and-hp-proliant-dl360-g5-scsi-controller-compatibility
|
||||
[11]: https://github.com/phips/ansible-hw-bootstrap/blob/master/roles/kickstart/templates/pxe_install.j2#L10
|
||||
[12]: https://github.com/phips/ansible-hw-bootstrap/blob/master/roles/kickstart/templates/local6.ks.j2#L3
|
||||
[13]: https://ipxe.org
|
||||
[14]: https://twitter.com/thismarkp
|
||||
[15]: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/installation_guide/ch-kickstart2
|
||||
[16]: https://github.com/phips/ansible-hw-bootstrap/blob/master/plays/reinstall.yml
|
||||
[17]: https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
|
||||
[18]: https://github.com/phips/ansible-hw-bootstrap/blob/master/plays/install.yml#L9
|
||||
[19]: https://docs.ansible.com/ansible/latest/modules/list_of_remote_management_modules.html
|
||||
[20]: https://docs.ansible.com/ansible/latest/modules/hpilo_boot_module.html#hpilo-boot-module
|
||||
[21]: https://github.com/phips/ansible-demos/tree/master/roles/phone_home
|
||||
@@ -1,284 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (The bits and bytes of PKI)
|
||||
[#]: via: (https://opensource.com/article/19/6/bits-and-bytes-pki)
|
||||
[#]: author: (Alex Wood https://opensource.com/users/awood)
|
||||
|
||||
The bits and bytes of PKI
|
||||
======
|
||||
Take a look under the public key infrastructure's hood to get a better
|
||||
understanding of its format.
|
||||
![Computer keyboard typing][1]
|
||||
|
||||
In two previous articles— _[An introduction to cryptography and public key infrastructure][2]_ and _[How do private keys work in PKI and cryptography?][3]_ —I discussed cryptography and public key infrastructure (PKI) in a general way. I talked about how digital bundles called _certificates_ store public keys and identifying information. These bundles contain a lot of complexity, and it's useful to have a basic understanding of the format for when you need to look under the hood.
|
||||
|
||||
### Abstract art
|
||||
|
||||
Keys, certificate signing requests, certificates, and other PKI artifacts define themselves in a data description language called [Abstract Syntax Notation One][4] (ASN.1). ASN.1 defines a series of simple data types (integers, strings, dates, etc.) along with some structured types (sequences, sets). By using those types as building blocks, we can create surprisingly complex data formats.
|
||||
|
||||
ASN.1 contains plenty of pitfalls for the unwary, however. For example, it has two different ways of representing dates: GeneralizedTime ([ISO 8601][5] format) and UTCTime (which uses a two-digit year). Strings introduce even more confusion. We have IA5String for ASCII strings and UTF8String for Unicode strings. ASN.1 also defines several other string types, from the exotic [T61String][6] and [TeletexString][7] to the more innocuous sounding—but probably not what you wanted—PrintableString (only a small subset of ASCII) and UniversalString (encoded in [UTF-32][8]). If you're writing or reading ASN.1 data, I recommend referencing the [specification][9].
|
||||
|
||||
ASN.1 has another data type worth special mention: the object identifier (OID). OIDs are a series of integers. Commonly they are shown with periods delimiting them. Each integer represents a node in what is basically a "tree of things." For example, [1.3.6.1.4.1.2312][10] is the OID for my employer, Red Hat, where "1" is the node for the International Organization for Standardization (ISO), "3" is for ISO-identified organizations, "6" is for the US Department of Defense (which, for historical reasons, is the parent to the next node), "1" is for the internet, "4" is for private organizations, "1" is for enterprises, and finally "2312," which is Red Hat's own.
|
||||
|
||||
More commonly, OIDs are regularly used to identify specific algorithms in PKI objects. If you have a digital signature, it's not much use if you don't know what type of signature it is. The signature algorithm "sha256WithRSAEncryption" has the OID "1.2.840.113549.1.1.11," for example.
|
||||
|
||||
### ASN.1 at work
|
||||
|
||||
Suppose we own a factory that produces flying brooms, and we need to store some data about every broom. Our brooms have a model name, a serial number, and a series of inspections that have been made to ensure flight-worthiness. We could store this information using ASN.1 like so:
|
||||
|
||||
|
||||
```
|
||||
BroomInfo ::= SEQUENCE {
|
||||
model UTF8String,
|
||||
serialNumber INTEGER,
|
||||
inspections SEQUENCE OF InspectionInfo
|
||||
}
|
||||
|
||||
InspectionInfo ::= SEQUENCE {
|
||||
inspectorName UTF8String,
|
||||
inspectionDate GeneralizedTime
|
||||
}
|
||||
```
|
||||
|
||||
The example above defines the model name as a UTF8-encoded string, the serial number as an integer, and our inspections as a series of InspectionInfo items. Then we see that each InspectionInfo item comprises two pieces of data: the inspector's name and the time of the inspection.
|
||||
|
||||
An actual instance of BroomInfo data would look something like this in ASN.1's value assignment syntax:
|
||||
|
||||
|
||||
```
|
||||
broom BroomInfo ::= {
|
||||
model "Nimbus 2000",
|
||||
serialNumber 1066,
|
||||
inspections {
|
||||
{
|
||||
inspectorName "Harry",
|
||||
inspectionDate "201901011200Z"
|
||||
}
|
||||
{
|
||||
inspectorName "Hagrid",
|
||||
inspectionDate "201902011200Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Don't worry too much about the particulars of the syntax; for the average developer, having a basic grasp of how the pieces fit together is sufficient.
|
||||
|
||||
Now let's look at a real example from [RFC 8017][11] that I have abbreviated somewhat for clarity:
|
||||
|
||||
|
||||
```
|
||||
RSAPrivateKey ::= SEQUENCE {
|
||||
version Version,
|
||||
modulus INTEGER, -- n
|
||||
publicExponent INTEGER, -- e
|
||||
privateExponent INTEGER, -- d
|
||||
prime1 INTEGER, -- p
|
||||
prime2 INTEGER, -- q
|
||||
exponent1 INTEGER, -- d mod (p-1)
|
||||
exponent2 INTEGER, -- d mod (q-1)
|
||||
coefficient INTEGER, -- (inverse of q) mod p
|
||||
otherPrimeInfos OtherPrimeInfos OPTIONAL
|
||||
}
|
||||
|
||||
Version ::= INTEGER { two-prime(0), multi(1) }
|
||||
(CONSTRAINED BY
|
||||
{-- version must be multi if otherPrimeInfos present --})
|
||||
|
||||
OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
|
||||
|
||||
OtherPrimeInfo ::= SEQUENCE {
|
||||
prime INTEGER, -- ri
|
||||
exponent INTEGER, -- di
|
||||
coefficient INTEGER -- ti
|
||||
}
|
||||
```
|
||||
|
||||
The ASN.1 above defines the PKCS #1 format used to store RSA keys. Looking at this, we can see the RSAPrivateKey sequence starts with a version type (either 0 or 1) followed by a bunch of integers and then an optional type called OtherPrimeInfos. The OtherPrimeInfos sequence contains one or more pieces of OtherPrimeInfo. And each OtherPrimeInfo is just a sequence of integers.
|
||||
|
||||
Let's look at an actual instance by asking OpenSSL to generate an RSA key and then pipe it into [asn1parse][12], which will print it out in a more human-friendly format. (By the way, the **genrsa** command I'm using here has been superseded by **genpkey** ; we'll see why a little later.)
|
||||
|
||||
|
||||
```
|
||||
% openssl genrsa 4096 2> /dev/null | openssl asn1parse
|
||||
0:d=0 hl=4 l=2344 cons: SEQUENCE
|
||||
4:d=1 hl=2 l= 1 prim: INTEGER :00
|
||||
7:d=1 hl=4 l= 513 prim: INTEGER :B80B0C2443...
|
||||
524:d=1 hl=2 l= 3 prim: INTEGER :010001
|
||||
529:d=1 hl=4 l= 512 prim: INTEGER :59C609C626...
|
||||
1045:d=1 hl=4 l= 257 prim: INTEGER :E8FC43002D...
|
||||
1306:d=1 hl=4 l= 257 prim: INTEGER :CA39222DD2...
|
||||
1567:d=1 hl=4 l= 256 prim: INTEGER :25F6CD181F...
|
||||
1827:d=1 hl=4 l= 256 prim: INTEGER :38CCE374CB...
|
||||
2087:d=1 hl=4 l= 257 prim: INTEGER :C80430E810...
|
||||
```
|
||||
|
||||
Recall that RSA uses a modulus, _n_ ; a public exponent, _e_ ; and a private exponent, _d_. Now let's look at the sequence. First, we see the version set to 0 for a two-prime RSA key (what **genrsa** generates), an integer for the modulus, _n_ , and then 0x010001 for the public exponent, _e_. If we convert to decimal, we'll see our public exponent is 65537, a number [commonly][13] used as an RSA public exponent. Following the public exponent, we see the integer for the private exponent, _e_ , and then some other integers that are used to speed up decryption and signing. Explaining how this optimization works is beyond the scope of this article, but if you like math, there's a [good video on the subject][14].
|
||||
|
||||
What about that other stuff on the left side of the output? What does "h=4" and "l=513" mean? We'll cover that shortly.
|
||||
|
||||
### DERangement
|
||||
|
||||
We've seen the "abstract" part of Abstract Syntax Notation One, but how does this data get encoded and stored? For that, we turn to a binary format called Distinguished Encoding Rules (DER) defined in the [X.690][15] specification. DER is a stricter version of its parent, Basic Encoding Rules (BER), in that for any given data, there is only one way to encode it. If we're going to be digitally signing data, it makes things a lot easier if there is only one possible encoding that needs to be signed instead of dozens of functionally equivalent representations.
|
||||
|
||||
DER uses a [tag-length-value][16] (TLV) structure. The encoding of a piece of data begins with an identifier octet defining the data's type. ("Octet" is used rather than "byte" since the standard is very old and some early architectures didn't use 8 bits for a byte.) Next are the octets that encode the length of the data, and finally, there is the data. The data can be another TLV series. The left side of the **asn1parse** output makes a little more sense now. The first number indicates the absolute offset from the beginning. The "d=" tells us the depth of that item in the structure. The first line is a sequence, which we descend into on the next line (the depth _d_ goes from 0 to 1) whereupon **asn1parse** begins enumerating all the elements in that sequence. The "hl=" is the header length (the sum of the identifier and length octets), and the "l=" tells us the length of that particular piece of data.
|
||||
|
||||
How is header length determined? It's the sum of the identifier byte and the bytes encoding the length. In our example, the top sequence is 2344 octets long. If it were less than 128 octets, the length would be encoded in a single octet in the "short form": bit 8 would be a zero and bits 7 to 1 would hold the length value ( **2 7-1=127**). A value of 2344 needs more space, so the "long" form is used. The first octet has bit 8 set to one, and bits 7 to 1 contain the length of the length. In our case, a value of 2344 can be encoded in two octets (0x0928). Combined with the first "length of the length" octet, we have three octets total. Add the one identifier octet, and that gives us our total header length of four.
|
||||
|
||||
As a side exercise, let's consider the largest value we could possibly encode. We've seen that we have up to 127 octets to encode a length. At 8 bits per octet, we have a total of 1008 bits to use, so we can hold a number equal to **2 1008-1**. That would equate to a content length of **2.743062*10 279** yottabytes, staggeringly more than the estimated **10 80** atoms in the observable universe. If you're interested in all the details, I recommend reading "[A Layman's Guide to a Subset of ASN.1, BER, and DER][17]."
|
||||
|
||||
What about "cons" and "prim"? Those indicate whether the value is encoded with "constructed" or "primitive" encoding. Primitive encoding is used for simple types like "INTEGER" or "BOOLEAN," while constructed encoding is used for structured types like "SEQUENCE" or "SET." The actual difference between the two encoding methods is whether bit 6 in the identifier octet is a zero or one. If it's a one, the parser knows that the content octets are also DER-encoded and it can descend.
|
||||
|
||||
### PEM pals
|
||||
|
||||
While useful in a lot of cases, a binary format won't pass muster if we need to display the data as text. Before the [MIME][18] standard existed, attachment support was spotty. Commonly, if you wanted to attach data, you put it in the body of the email, and since SMTP only supported ASCII, that meant converting your binary data (like the DER of your public key, for example) into ASCII characters.
|
||||
|
||||
Thus, the PEM format emerged. PEM stands for "Privacy-Enhanced Email" and was an early standard for transmitting and storing PKI data. The standard never caught on, but the format it defined for storage did. PEM-encoded objects are just DER objects that are [base64][19]-encoded and wrapped at 64 characters per line. To describe the type of object, a header and footer surround the base64 string. You'll see **\-----BEGIN CERTIFICATE-----** or **\-----BEGIN PRIVATE KEY-----** , for example.
|
||||
|
||||
Often you'll see files with the ".pem" extension. I don't find this suffix useful. The file could contain a certificate, a key, a certificate signing request, or several other possibilities. Imagine going to a sushi restaurant and seeing a menu that described every item as "fish and rice"! Instead, I prefer more informative extensions like ".crt", ".key", and ".csr".
|
||||
|
||||
### The PKCS zoo
|
||||
|
||||
Earlier, I showed an example of a PKCS #1-formatted RSA key. As you might expect, formats for storing certificates and signing requests also exist in various IETF RFCs. For example, PKCS #8 can be used to store private keys for many different algorithms (including RSA!). Here's some of the ASN.1 from [RFC 5208][20] for PKCS #8. (RFC 5208 has been obsoleted by RFC 5958, but I feel that the ASN.1 in RFC 5208 is easier to understand.)
|
||||
|
||||
|
||||
```
|
||||
PrivateKeyInfo ::= SEQUENCE {
|
||||
version Version,
|
||||
privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
|
||||
privateKey PrivateKey,
|
||||
attributes [0] IMPLICIT Attributes OPTIONAL }
|
||||
|
||||
Version ::= INTEGER
|
||||
|
||||
PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
|
||||
|
||||
PrivateKey ::= OCTET STRING
|
||||
|
||||
Attributes ::= SET OF Attribute
|
||||
```
|
||||
|
||||
If you store your RSA private key in a PKCS #8, the PrivateKey element will actually be a DER-encoded PKCS #1! Let's prove it. Remember earlier when I used **genrsa** to generate a PKCS #1? OpenSSL can generate a PKCS #8 with the **genpkey** command, and you can specify RSA as the algorithm to use.
|
||||
|
||||
|
||||
```
|
||||
% openssl genpkey -algorithm RSA | openssl asn1parse
|
||||
0:d=0 hl=4 l= 629 cons: SEQUENCE
|
||||
4:d=1 hl=2 l= 1 prim: INTEGER :00
|
||||
7:d=1 hl=2 l= 13 cons: SEQUENCE
|
||||
9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
|
||||
20:d=2 hl=2 l= 0 prim: NULL
|
||||
22:d=1 hl=4 l= 607 prim: OCTET STRING [HEX DUMP]:3082025B...
|
||||
```
|
||||
|
||||
You may have spotted the "OBJECT" in the output and guessed that was related to OIDs. You'd be correct. The OID "1.2.840.113549.1.1.1" is assigned to RSA encryption. OpenSSL has a built-in list of common OIDs and translates them into a human-readable form for you.
|
||||
|
||||
|
||||
```
|
||||
% openssl genpkey -algorithm RSA | openssl asn1parse -strparse 22
|
||||
0:d=0 hl=4 l= 604 cons: SEQUENCE
|
||||
4:d=1 hl=2 l= 1 prim: INTEGER :00
|
||||
7:d=1 hl=3 l= 129 prim: INTEGER :CA6720E706...
|
||||
139:d=1 hl=2 l= 3 prim: INTEGER :010001
|
||||
144:d=1 hl=3 l= 128 prim: INTEGER :05D0BEBE44...
|
||||
275:d=1 hl=2 l= 65 prim: INTEGER :F215DC6B77...
|
||||
342:d=1 hl=2 l= 65 prim: INTEGER :D6095CED7E...
|
||||
409:d=1 hl=2 l= 64 prim: INTEGER :402C7562F3...
|
||||
475:d=1 hl=2 l= 64 prim: INTEGER :06D0097B2D...
|
||||
541:d=1 hl=2 l= 65 prim: INTEGER :AB266E8E51...
|
||||
```
|
||||
|
||||
In the second command, I've told **asn1parse** via the **-strparse** argument to move to octet 22 and begin parsing the content's octets there as an ASN.1 object. We can clearly see that the PKCS #8's PrivateKey looks just like the PKCS #1 that we examined earlier.
|
||||
|
||||
You should favor using the **genpkey** command. PKCS #8 has some features that PKCS #1 does not: PKCS #8 can store private keys for multiple different algorithms (PKCS #1 is RSA-specific), and it provides a mechanism to encrypt the private key using a passphrase and a symmetric cipher.
|
||||
|
||||
Encrypted PKCS #8 objects use a different ASN.1 syntax that I'm not going to dive into, but let's take a look at an actual example and see if anything stands out. Encrypting a private key with **genpkey** requires that you specify the symmetric encryption algorithm to use. I'll use AES-256-CBC for this example and a password of "hello" (the "pass:" prefix is the way of telling OpenSSL that the password is coming in from the command line).
|
||||
|
||||
|
||||
```
|
||||
% openssl genpkey -algorithm RSA -aes-256-cbc -pass pass:hello | openssl asn1parse
|
||||
0:d=0 hl=4 l= 733 cons: SEQUENCE
|
||||
4:d=1 hl=2 l= 87 cons: SEQUENCE
|
||||
6:d=2 hl=2 l= 9 prim: OBJECT :PBES2
|
||||
17:d=2 hl=2 l= 74 cons: SEQUENCE
|
||||
19:d=3 hl=2 l= 41 cons: SEQUENCE
|
||||
21:d=4 hl=2 l= 9 prim: OBJECT :PBKDF2
|
||||
32:d=4 hl=2 l= 28 cons: SEQUENCE
|
||||
34:d=5 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:17E6FE554E85810A
|
||||
44:d=5 hl=2 l= 2 prim: INTEGER :0800
|
||||
48:d=5 hl=2 l= 12 cons: SEQUENCE
|
||||
50:d=6 hl=2 l= 8 prim: OBJECT :hmacWithSHA256
|
||||
60:d=6 hl=2 l= 0 prim: NULL
|
||||
62:d=3 hl=2 l= 29 cons: SEQUENCE
|
||||
64:d=4 hl=2 l= 9 prim: OBJECT :aes-256-cbc
|
||||
75:d=4 hl=2 l= 16 prim: OCTET STRING [HEX DUMP]:91E9536C39...
|
||||
93:d=1 hl=4 l= 640 prim: OCTET STRING [HEX DUMP]:98007B264F...
|
||||
|
||||
% openssl genpkey -algorithm RSA -aes-256-cbc -pass pass:hello | head -n 1
|
||||
\-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
```
|
||||
|
||||
There are a couple of interesting items here. We see our encryption algorithm is recorded with an OID starting at octet 64. There's an OID for "PBES2" (Password-Based Encryption Scheme 2), which defines a standard process for encryption and decryption, and an OID for "PBKDF2" (Password-Based Key Derivation Function 2), which defines a standard process for creating encryption keys from passwords. Helpfully, OpenSSL uses the header "ENCRYPTED PRIVATE KEY" in the PEM output.
|
||||
|
||||
OpenSSL will let you encrypt a PKCS #1, but it's done in a non-standard way via a series of headers inserted into the PEM:
|
||||
|
||||
|
||||
```
|
||||
% openssl genrsa -aes256 -passout pass:hello 4096
|
||||
\-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-256-CBC,5B2C64DC05B7C0471A278C76562FD776
|
||||
...
|
||||
```
|
||||
|
||||
### In conclusion
|
||||
|
||||
There's a final PKCS format you need to know about: [PKCS #12][21]. The PKCS #12 format allows for storing multiple objects all in one file. If you have a certificate and its corresponding key or a chain of certificates, you can store them together in one PKCS #12 file. Individual entries in the file can be protected with password-based encryption.
|
||||
|
||||
Beyond the PKCS formats, there are other storage methods such as the Java-specific JKS format and the NSS library from Mozilla, which uses file-based databases (SQLite or Berkeley DB, depending on the version). Luckily, the PKCS formats are a lingua franca that can serve as a start or reference if you need to deal with other formats.
|
||||
|
||||
If this all seems confusing, that's because it is. Unfortunately, the PKI ecosystem has a lot of sharp edges between tools that generate enigmatic error messages (looking at you, OpenSSL) and standards that have grown and evolved over the past 35 years. Having a basic understanding of how PKI objects are stored is critical if you're doing any application development that will be accessed over SSL/TLS.
|
||||
|
||||
I hope this article has shed a little light on the subject and might save you from spending fruitless hours in the PKI wilderness.
|
||||
|
||||
* * *
|
||||
|
||||
_The author would like to thank Hubert Kario for providing a technical review._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/6/bits-and-bytes-pki
|
||||
|
||||
作者:[Alex Wood][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/awood
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/keyboaord_enter_writing_documentation.jpg?itok=kKrnXc5h (Computer keyboard typing)
|
||||
[2]: https://opensource.com/article/18/5/cryptography-pki
|
||||
[3]: https://opensource.com/article/18/7/private-keys
|
||||
[4]: https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
|
||||
[5]: https://en.wikipedia.org/wiki/ISO_8601
|
||||
[6]: https://en.wikipedia.org/wiki/ITU_T.61
|
||||
[7]: https://en.wikipedia.org/wiki/Teletex
|
||||
[8]: https://en.wikipedia.org/wiki/UTF-32
|
||||
[9]: https://www.itu.int/itu-t/recommendations/rec.aspx?rec=X.680
|
||||
[10]: https://www.alvestrand.no/objectid/1.3.6.1.4.1.2312.html
|
||||
[11]: https://tools.ietf.org/html/rfc8017
|
||||
[12]: https://linux.die.net/man/1/asn1parse
|
||||
[13]: https://www.johndcook.com/blog/2018/12/12/rsa-exponent/
|
||||
[14]: https://www.youtube.com/watch?v=NcPdiPrY_g8
|
||||
[15]: https://en.wikipedia.org/wiki/X.690
|
||||
[16]: https://en.wikipedia.org/wiki/Type-length-value
|
||||
[17]: http://luca.ntop.org/Teaching/Appunti/asn1.html
|
||||
[18]: https://www.theguardian.com/technology/2012/mar/26/ather-of-the-email-attachment
|
||||
[19]: https://en.wikipedia.org/wiki/Base64
|
||||
[20]: https://tools.ietf.org/html/rfc5208
|
||||
[21]: https://tools.ietf.org/html/rfc7292
|
||||
@@ -1,222 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Linux Package Managers Compared – AppImage vs Snap vs Flatpak)
|
||||
[#]: via: (https://www.ostechnix.com/linux-package-managers-compared-appimage-vs-snap-vs-flatpak/)
|
||||
[#]: author: (editor https://www.ostechnix.com/author/editor/)
|
||||
|
||||
Linux Package Managers Compared – AppImage vs Snap vs Flatpak
|
||||
======
|
||||
|
||||
![3 Linux Package Managers Compared][1]
|
||||
|
||||
**Package managers** provide a way of packaging, distributing, installing, and maintaining apps in an operating system. With modern desktop, server and IoT applications of the Linux operating system and the hundreds of different distros that exist, it becomes necessary to move away from platform specific packaging methods to platform agnostic ones. This post explores 3 such tools, namely **AppImage** , **Snap** and **Flatpak** , that each aim to be the future of software deployment and management in Linux. At the end we summarize a few key findings.
|
||||
|
||||
### 1\. AppImage
|
||||
|
||||
**AppImage** follows a concept called **“One app = one file”**. This is to be understood as an AppImage being a regular independent “file” containing one application with everything it needs to run in the said file. Once made executable, the AppImage can be run like any application in a computer by simply double-clicking it in the users file system.[1]
|
||||
|
||||
It is a format for creating portable software for Linux without requiring the user to install the said application. The format allows the original developers of the software (upstream developers) to create a platform and distribution independent (also called a distribution-agnostic binary) version of their application that will basically run on any flavor of Linux.
|
||||
|
||||
AppImage has been around for a long time. **Klik** , a predecessor of AppImage was created by **Simon Peter** in 2004. The project was shut down in 2011 after not having passed the beta stage. A project named **PortableLinuxApps** was created by Simon around the same time and the format was picked up by a few portals offering software for Linux users. The project was renamed again in 2013 to its current name AppImage and a repository has been maintained in GitHub (project [link][2]) with all the latest changes to the same since 2018.[2][3]
|
||||
|
||||
Written primarily in **C** and donning the **MIT license** since 2013, AppImage is currently developed by **The AppImage project**. It is a very convenient way to use applications as demonstrated by the following features:
|
||||
|
||||
1. AppImages can run on virtually any Linux system. As mentioned before applications derive a lot of functionality from the operating system and a few common libraries. This is a common practice in the software world since if something is already done, there is no point in doing it again if you can pick and choose which parts from the same to use. The problem is that many Linux distros might not have all the files a particular application requires to run since it is left to the developers of that particular distro to include the necessary packages. Hence developers need to separately include the dependencies of the application for each Linux distro they are publishing their app for. Using the AppImage format developers can choose to include all the libraries and files that they cannot possibly hope the target operating system to have as part of the AppImage file. Hence the same AppImage format file can work on different operating systems and machines without needing granular control.
|
||||
2. The one app one file philosophy means that user experience is simple and elegant in that users need only download and execute one file that will serve their needs for using the application.
|
||||
3. **No requirement of root access**. System administrators will require people to have root access to stop them from messing with computers and their default setup. This also means that people with no root access or super user privileges cannot install the apps they need as they please. The practice is common in a public setting (such as library or university computers or on enterprise systems). The AppImage file does not require users to “install” anything and hence users need only download the said file and **make it executable** to start using it. This removes the access dilemmas that system administrators have and makes their job easier without sacrificing user experience.
|
||||
4. **No effect on core operating system**. The AppImage-application format allows using applications with their full functionality without needing to change or even access most system files. Meaning whatever the applications do, the core operating system setup and files remain untouched.
|
||||
5. An AppImage can be made by a developer for a particular version of their application. Any updated version is made as a different AppImage. Hence users if need be can **test multiple versions of the same application** by running different instances using different AppImages. This is an invaluable feature when you need to test your applications from an end-user POV to notice differences.
|
||||
6. Take your applications where you go. As mentioned previously AppImages are archived files of all the files that an application requires and can be used without installing or even bothering about the distribution the system uses. Hence if you have a set of apps that you use regularly you may even mount a few AppImage files on a thumb drive and take it with you to use on multiple computers running multiple different distros without worrying whether they’ll work or not.
|
||||
|
||||
|
||||
|
||||
Furthermore, the **AppImageKit** allows users from all backgrounds to build their own AppImages from applications they already have or for applications that are not provided an AppImage by their upstream developer.
|
||||
|
||||
The package manager is platform independent but focuses primarily on software distribution to end users on their desktops with a dedicated daemon **AppImaged** for integrating the AppImage formats into respective desktop environments. AppImage is supported natively now by a variety of distros such as Ubuntu, Debian, openSUSE, CentOS, Fedora etc. and others may set it up as per their needs. AppImages can also be run on servers with limited functionality via the CLI tools included.
|
||||
|
||||
To know more about AppImages, go to the official [**AppImage documentation**][3] page.
|
||||
|
||||
* * *
|
||||
|
||||
**Suggested read:**
|
||||
|
||||
* [**Search Linux Applications On AppImage, Flathub And Snapcraft Platforms**][4]
|
||||
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
### 2\. Snappy
|
||||
|
||||
**Snappy** is a software deployment and package management system like AppImage or any other package manager for that instance. It is originally designed for the now defunct **Ubuntu Touch** Operating system. Snappy lets developers create software packages for use in a variety of Linux based distributions. The initial intention behind creating Snappy and deploying **“snaps”** on Ubuntu based systems is to obtain a unified single format that could be used in everything from IoT devices to full-fledged computer systems that ran some version of Ubuntu and in a larger sense Linux itself.[4]
|
||||
|
||||
The lead developer behind the project is **Canonical** , the same company that pilots the Ubuntu project. Ubuntu had native snap support from version 16.04 LTS with more and more distros supporting it out of the box or via a simple setup these days. If you use Arch or Debian or openSUSE you’ll find it easy to install support for the package manager using simple commands in the terminal as explained later in this section. This is also made possible by making the necessary snap platform files available on the respective repos.[5]
|
||||
|
||||
Snappy has the following important components that make up the entire package manager system.[6]
|
||||
|
||||
* **Snap** – is the file format of the packages themselves. Individual applications that are deployed using Snappy are called “Snaps”. Any application may be packaged using the tools provided to make a snap that is intended to run on a different system running Linux. Snap, similar to AppImage is an all-inclusive file and contains all dependencies the application needs to run without assuming them to part of the target system.
|
||||
* **Snapcraft** – is the tool that lets developers make snaps of their applications. It is basically a command that is part of the snap system as well as a framework that will let you build your own snaps.
|
||||
* **Snapd** – is the background daemon that maintains all the snaps that are installed in your system. It integrates into the desktop environment and manages all the files and processes related to working with snaps. The snapd daemon also checks for updates normally **4 times a day** unless set otherwise.
|
||||
* [**Snap Store**][5] – is an online gallery of sorts that lets developers upload their snaps into the repository. Snap store is also an application discovery medium for users and will let users see and experience the application library before downloading and installing them.
|
||||
|
||||
|
||||
|
||||
The snapd component is written primarily in **C** and **Golang** whereas the Snapcraft framework is built using **Python**. Although both the modules use the GPLv3 license it is to be noted that snapd has proprietary code from Canonical for its server-side operations with just the client side being published under the GPL license. This is a major point of contention with developers since this involves developers signing a CLA form to participate in snap development.[7]
|
||||
|
||||
Going deeper into the finer details of the Snappy package manager the following may be noted:
|
||||
|
||||
1. Snaps as noted before are all inclusive and contain all the necessary files (dependencies) that the application needs to run. Hence, developers need not to make different snaps for the different distros that they target. Being mindful of the runtimes is all that’s necessary if base runtimes are excluded from the snap.
|
||||
2. Snappy packages are meant to support transactional updates. Such a transactional update is atomic and fully reversible, meaning you can use the application while its being updated and that if an update does not behave the way its supposed to, you can reverse the same with no other effects whatsoever. The concept is also called as **delta programming** in which only changes to the application are transmitted as an update instead of the whole package. An Ubuntu derivative called **Ubuntu Core** actually promises the snappy update protocol to the OS itself.[8]
|
||||
3. A key point of difference between snaps and AppImages, is how they handle version differences. Using AppImages different versions of the application will have different AppImages allowing you to concurrently use 2 or more different versions of the same application at the same time. However, using snaps means conforming to the transactional or delta update system. While this means faster updates, it keeps you from running two instances of the same application at the same time. If you need to use the old version of an app you’ll need to reverse or uninstall the new version. Snappy does support a feature called [**“parallel install”**][6] which will let users accomplish similar goals, however, it is still in an experimental stage and cannot be considered to be a stable implementation. Snappy also makes use of channels meaning you can use the beta or the nightly build of an app and the stable version at the same time.[9]
|
||||
4. Extensive support from major Linux distros and major developers including Google, Mozilla, Microsoft, etc.[4]
|
||||
5. Snapd the desktop integration tool supports taking **“snapshots”** of the current state of all the installed snaps in the system. This will let users save the current configuration state of all the applications that are installed via the Snappy package manager and let users revert to that state whenever they desire so. The same feature can also be set to automatically take snapshots at a frequency deemed necessary by the user. Snapshots can be created using the **snap save command** in the snapd framework.[10]
|
||||
6. Snaps are designed to be sandboxed during operation. This provides a much-required layer of security and isolation to users. Users need not worry about snap-based applications messing with the rest of the software on their computer. Sandboxing is implemented using three levels of isolation viz, **classic** , **strict** and **devmode**. Each level of isolation allows the app different levels of access within the file system and computer.[11]
|
||||
|
||||
|
||||
|
||||
On the flip side of things, snaps are widely criticized for being centered around **Canonical’s modus operandi**. Most of the commits to the project are by Canonical employees or contractors and other contributors are required to sign a release form (CLA). The sandboxing feature, a very important one indeed from a security standpoint, is flawed in that the sandboxing actually requires certain other core services to run (such as Mir) while applications running the X11 desktop won’t support the said isolation, hence making the said security feature irrelevant. Questionable press releases and other marketing efforts from Canonical and the “central” and closed app repository are also widely criticized aspects of Snappy. Furthermore, the file sizes of the different snaps are also **comparatively very large** compared to the app sizes of the packages made using AppImage.[7]
|
||||
|
||||
For more details, check [**Snap official documentation**][7].
|
||||
|
||||
* * *
|
||||
|
||||
**Related read:**
|
||||
|
||||
* [**Install Snap packages in Arch Linux, and Fedora**][8]
|
||||
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
### 3\. Flatpak
|
||||
|
||||
Like the Snap/Snappy listed above, **Flatpak** is also a software deployment tool that aims to ease software distribution and use in Linux. Flatpak was previously known as **“xdg-app”** and was based on concept proposed by **Lennart Poettering** in 2004. The idea was to contain applications in a secure virtual sandbox allowing for using applications **without the need of root privileges** and without compromising on the systems security. **Alex** started tinkering with Klik (thought to be a former version of AppImage) and wanted to implement the concept better. **Alexander Larsson** who at the time was working with Red Hat wrote an implementation called xdg-app in 2015 that acted as a pre-cursor to the current Flatpak format.
|
||||
|
||||
Flatpak officially came out in 2016 with backing from Red Hat, Endless Computers and Collabora. **Flathub** is the official repository of all Flatpak application packages. At its surface Flatpak like the other is a framework for building and packaging distribution agnostic applications for Linux. It simply requires the developers to conform to a few desktop environment guidelines in order for the application to be successfully integrated into the Flatpak environment.
|
||||
|
||||
Targeted primarily at the three popular desktop implementations **FreeDesktop** , **KDE** , and **GNOME** , the Flatpak framework itself is written in **C** and works on a **LGPL** license. The maintenance repository can be accessed via the GitHub link **[here][9]**.
|
||||
|
||||
A few features of Flatpak that make it stand apart are mentioned below. Notice that features Flatpak shares with AppImage and Snappy are omitted here.
|
||||
|
||||
* Deep integration into popular Linux desktop environments such as GNOME & KDE so that users can simply use Flatpaks using Graphical software management tools instead of resorting to the terminal. Flatpak can be installed from the default repositories of major desktop environments now and once the apps themselves are set-up they can be used and provide features similar to normal desktop applications.[12][13]
|
||||
* **Forward-compatibility** – Flatpaks are built from the ground up keeping the operating systems core kernel and runtimes in mind. Hence, even if you upgrade or update your distro the Flatpaks you have should still work unless there is a core update. This is especially crucial for people who prefer staying on rolling betas or development versions of their distros. For such people, since the kinks of the OS itself isn’t ironed out usually, the Flatpak application will run seamlessly without having to depend on the OS files or libraries for its operation.[13]
|
||||
* **Sandboxing using Bubblewrap** – snaps are also by default sandboxed in that they run in isolation from the rest of the applications running while you’re using your computer. However, Flatpaks fully seal the application from accessing OS files and user files during its operation by default. This essentially means that system administrators can be certain that Flatpaks that are installed in their systems cannot exploit the computer and the files it contains whereas for end users this will mean that in order to access a few specific functions or user data root permission is required.[14]
|
||||
* Flatpak supports decentralized distribution of application natively however the team behind Flatpak still maintains a central online repository of apps/Flatpaks called **Flathub**. Users may in fact configure Flatpak to use multiple remote repositories as they see necessary. As opposed to snap you can have multiple repositories.[13]
|
||||
* Modular access through the sandbox. Although this capability comes at a great potential cost to the integrity of the system, Flatpak framework allows for channels to be created through the sandbox for exchange of specific information from within the sandbox to the host system or vice versa. The channel is in this case referred to as a portal. A con to this feature is discussed later in the section.[14]
|
||||
|
||||
|
||||
|
||||
One of the most criticized aspects of Flatpak however is it’s the sandbox feature itself. Sandboxing is how package managers such as Snappy and Flatpak implement important security features. Sandboxing essentially isolates the application from everything else in the system only allowing for user defined exchange of information from within the sandbox to outside. The flaw with the concept being that the sandbox cannot be inherently impregnable. Data has to be eventually transferred between the two domains and simple Linux commands can simply get rid of the sandbox restriction meaning that malicious applications might potentially jump out of the said sandbox.[15]
|
||||
|
||||
This combined with the worse than expected commitment to rolling out security updates for Flatpak has resulted in widespread criticism of the team’s tall claim of providing a secure framework. The blog (named **flatkill** ) linked at the end of this guide in fact mentions a couple of exploits that were not addressed by the Flatpak team as soon as they should’ve been.[15]
|
||||
|
||||
For more details, I suggest you to read [**Flatpak official documentation**][10].
|
||||
|
||||
* * *
|
||||
|
||||
**Related read:**
|
||||
|
||||
* [**A Beginners Guide To Flatpak**][11]
|
||||
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
### AppImage vs Snap vs Flatpak
|
||||
|
||||
The table attached below summarizes all the above findings into a concise and technical comparison of the three frameworks.
|
||||
|
||||
**Feature** | **AppImage** | **Snappy** | **Flatpak**
|
||||
---|---|---|---
|
||||
**Unique feature** | Not an appstore or repository, its simply put a packaging format for software distribution. | Led by Canonical (Same company as Ubuntu), features central app repository and active contribution from Canonical. | Features an app store called FlatHub, however, individuals may still host packages and distribute it.
|
||||
**Target system** | Desktops and Servers. | Desktops, Servers, IoT devices, Embedded devices etc. | Desktops and limited function on servers.
|
||||
**Libraries/Dependencies** | Base system. Runtimes optional, Libraries and other dependencies packaged. | Base system or via Plugins or can be packaged. | GNOME, KDE, Freedesktop bundled or custom bundled.
|
||||
**Developers** | Community Driven led by Simon Peter. | Corporate driven by Canonical Ltd. | Community driven by flatpak team supported by enterprise.
|
||||
**Written in** | C. | Golang, C and Python. | C.
|
||||
**Initial release** | 2004. | 2014. | 2015.
|
||||
**Sandboxing** | Can be implemented. | 3 modes – strict, classic, and devmode with varying confinement capabilities. Runs in isolation. | Isolated but Uses system files to run applications by default.
|
||||
**Sandboxing Platform** | Firejail, AppArmor, Bubblewrap. | AppArmor. | Bubblewrap.
|
||||
**App Installation** | Not necessary. Will act as self mounted disc. | Installation using snapd. | Installed using flatpak client tools.
|
||||
**App Execution** | Can be run after setting executing bit. | Using desktop integrated snap tools. Runs isolated with user defined resources. | Needs to be executed using flatpak command if CLI is used.
|
||||
**User Privileges** | Can be run w/o root user access. | Can be run w/o root user access. | Selectively required.
|
||||
**Hosting Applications** | Can be hosted anywhere by anybody. | Has to be hosted with Canonical servers which are proprietary. | Can be hosted anywhere by anybody.
|
||||
**Portable Execution from non system locations** | Yes. | No. | Yes, after flatpak client is configured.
|
||||
**Central Repository** | AppImageHub. | Snap Store. | Flathub.
|
||||
**Running multiple versions of the app** | Possible, any number of versions simultaneously. | One version of the app in one channel. Has to be separately configured for more. | Yes.
|
||||
**Updating applications** | Using CLI command AppImageUpdate or via an updater tool built into the AppImage. | Requires snapd installed. Supports delta updating, will automatically update. | Required flatpak installed. Update Using flatpak update command.
|
||||
**Package sizes on disk** | Application remains archived. | Application remains archived. | Client side is uncompressed.
|
||||
|
||||
Here is a long tabular comparison of AppImage vs. Snap vs. Flatpak features. Please note that the comparison is made from an AppImage perspective.
|
||||
|
||||
* [**https://github.com/AppImage/AppImageKit/wiki/Similar-projects#comparison**][12]
|
||||
|
||||
|
||||
|
||||
### Conclusion
|
||||
|
||||
While all three of these platforms have a lot in common with each other and aim to be platform agnostic in approach, they offer different levels of competencies in a few areas. While Snaps can run on a variety of devices including embedded ones, AppImages and Flatpaks are built with the desktop user in mind. AppImages of popular applications on the other had have superior packaging sizes and portability whereas Flatpak really shines with its forward compatibility when its used in a set it and forget it system.
|
||||
|
||||
If there are any flaws in this guide, please let us know in the comment section below. We will update the guide accordingly.
|
||||
|
||||
**References:**
|
||||
|
||||
* **[1]** [**Concepts — AppImage documentation**][13]
|
||||
* **[2]** [**Slashdot – Point-and-klik Linux Software Installation**][14]
|
||||
* **[3]** [**History of AppImage project**][15]
|
||||
* **[4][Snapcraft – Snaps are universal Linux packages][16]**
|
||||
* **[5][Installing snapd – Snap documentation][17]**
|
||||
* **[6][Snap documentation][7]**
|
||||
* **[7][On Snappy and Flatpak: business as usual in the Canonical propaganda department][18]**
|
||||
* **[8][Snap Updates are getting smaller, here’s why][19]**
|
||||
* **[9][What Are Linux Snap Packages? Why Use Them?][20]**
|
||||
* **[10][Snapshots – Snap documentation][21]**
|
||||
* **[11][Snap confinement – Snap documentation][22]**
|
||||
* **[12][Desktop Integration – Flatpak documentation][23]**
|
||||
* **[13][Introduction to Flatpak – Flatpak documentation][24]**
|
||||
* **[14][Sandbox Permissions – Flatpak documentation][25]**
|
||||
* **[15][Flatpak – a security nightmare][26]**
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/linux-package-managers-compared-appimage-vs-snap-vs-flatpak/
|
||||
|
||||
作者:[editor][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/editor/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2019/06/Linux-Package-Managers-Compared-1-720x340.png
|
||||
[2]: https://github.com/AppImage/AppImageKit/blob/master/README.md
|
||||
[3]: https://docs.appimage.org/
|
||||
[4]: https://www.ostechnix.com/search-linux-applications-on-appimage-flathub-and-snapcraft-platforms/
|
||||
[5]: https://snapcraft.io/store
|
||||
[6]: https://blog.ubuntu.com/2019/06/20/parallel-installs-test-and-run-multiple-instances-of-snaps
|
||||
[7]: https://docs.snapcraft.io/
|
||||
[8]: https://www.ostechnix.com/install-snap-packages-arch-linux-fedora/
|
||||
[9]: https://github.com/flatpak/flatpak
|
||||
[10]: http://docs.flatpak.org/en/latest/index.html
|
||||
[11]: https://www.ostechnix.com/flatpak-new-framework-desktop-applications-linux/
|
||||
[12]: https://github.com/AppImage/AppImageKit/wiki/Similar-projects#comparison
|
||||
[13]: https://docs.appimage.org/introduction/concepts.html#one-app-one-file.
|
||||
[14]: https://linux.slashdot.org/story/05/01/15/1815210/point-and-klik-linux-software-installation
|
||||
[15]: https://github.com/AppImage/AppImageKit/wiki/History#timeline.
|
||||
[16]: https://snapcraft.io/#
|
||||
[17]: https://docs.snapcraft.io/installing-snapd
|
||||
[18]: https://www.happyassassin.net/2016/06/16/on-snappy-and-flatpak-business-as-usual-in-the-canonical-propaganda-department/
|
||||
[19]: https://blog.ubuntu.com/2017/08/01/snap-updates-are-getting-smaller-heres-why
|
||||
[20]: https://www.feliciano.tech/blog/what-are-linux-snap-packages-why-use-them/
|
||||
[21]: https://docs.snapcraft.io/snapshots
|
||||
[22]: https://docs.snapcraft.io/snap-confinement
|
||||
[23]: http://docs.flatpak.org/en/latest/desktop-integration.html?highlight=desktop%20integration
|
||||
[24]: http://docs.flatpak.org/en/latest/introduction.html
|
||||
[25]: http://docs.flatpak.org/en/latest/sandbox-permissions.html?highlight=sandboxing
|
||||
[26]: https://flatkill.org/
|
||||
@@ -1,130 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (12 extensions for your GNOME desktop)
|
||||
[#]: via: (https://opensource.com/article/19/8/extensions-gnome-desktop)
|
||||
[#]: author: (Alan Formy-Duval https://opensource.com/users/alanfdosshttps://opensource.com/users/erezhttps://opensource.com/users/alanfdosshttps://opensource.com/users/patrickhttps://opensource.com/users/liamnairn)
|
||||
|
||||
12 extensions for your GNOME desktop
|
||||
======
|
||||
Add functionality and features to your Linux desktop with these add-ons.
|
||||
![A person working.][1]
|
||||
|
||||
The GNOME desktop is the default graphical user interface for most of the popular Linux distributions and some of the BSD and Solaris operating systems. Currently at version 3, GNOME provides a sleek user experience, and extensions are available for additional functionality.
|
||||
|
||||
We've covered [GNOME extensions][2] at Opensource.com before, but to celebrate GNOME's 22nd anniversary, I decided to revisit the topic. Some of these extensions may already be installed, depending on your Linux distribution; if not, check your package manager.
|
||||
|
||||
### How to add extensions from the package manager
|
||||
|
||||
To install extensions that aren't in your distro, open the package manager and click **Add-ons**. Then click **Shell Extensions** at the top-right of the Add-ons screen, and you will see a button for **Extension Settings** and a list of available extensions.
|
||||
|
||||
![Package Manager Add-ons Extensions view][3]
|
||||
|
||||
Use the Extension Settings button to enable, disable, or configure the extensions you have installed.
|
||||
|
||||
Now that you know how to add and enable extensions, here are some good ones to try.
|
||||
|
||||
## 1\. GNOME Clocks
|
||||
|
||||
[GNOME Clocks][4] is an application that includes a world clock, alarm, stopwatch, and timer. You can configure clocks for different geographic locations. For example, if you regularly work with colleagues in another time zone, you can set up a clock for their location. You can access the World Clocks section in the top panel's drop-down menu by clicking the system clock. It shows your configured world clocks (not including your local time), so you can quickly check the time in other parts of the world.
|
||||
|
||||
## 2\. GNOME Weather
|
||||
|
||||
[GNOME Weather][5] displays the weather conditions and forecast for your current location. You can access local weather conditions from the top panel's drop-down menu. You can also check the weather in other geographic locations using Weather's Places menu.
|
||||
|
||||
GNOME Clocks and Weather are small applications that have extension-like functionality. Both are installed by default on Fedora 30 (which is what I'm using). If you're using another distribution and don't see them, check the package manager.
|
||||
|
||||
You can see both extensions in action in the image below.
|
||||
|
||||
![Clocks and Weather shown in the drop-down][6]
|
||||
|
||||
## 3\. Applications Menu
|
||||
|
||||
I think the GNOME 3 interface is perfectly enjoyable in its stock form, but you may prefer a traditional application menu. In GNOME 30, the [Applications Menu][7] extension was installed by default but not enabled. To enable it, click the Extensions Settings button in the Add-ons section of the package manager and enable the Applications Menu extension.
|
||||
|
||||
![Extension Settings][8]
|
||||
|
||||
Now you can see the Applications Menu in the top-left corner of the top panel.
|
||||
|
||||
![Applications Menu][9]
|
||||
|
||||
## 4\. More columns in applications view
|
||||
|
||||
The Applications view is set by default to six columns of icons, probably because GNOME needs to accommodate a wide array of displays. If you're using a wide-screen display, you can use the [More columns in applications menu][10] extension to increase the columns. I find that setting it to eight makes better use of my screen by eliminating the empty columns on either side of the icons when I launch the Applications view.
|
||||
|
||||
## Add system info to the top panel
|
||||
|
||||
The next three extensions provide basic system information to the top panel.
|
||||
|
||||
* 5. [Harddisk LED][11] shows a small hard drive icon with input/output (I/O) activity.
|
||||
* 6. [Load Average][12] indicates Linux load averages taken over three time intervals.
|
||||
* 7. [Uptime Indicator][13] shows system uptime; when it's clicked, it shows the date and time the system was started.
|
||||
|
||||
|
||||
|
||||
## 8\. Sound Input and Output Device Chooser
|
||||
|
||||
Your system may have more than one audio device for input and output. For example, my laptop has internal speakers and sometimes I use a wireless Bluetooth speaker. The [Sound Input and Output Device Chooser][14] extension adds a list of your sound devices to the System Menu so you can quickly select which one you want to use.
|
||||
|
||||
## 9\. Drop Down Terminal
|
||||
|
||||
Fellow Opensource.com writer [Scott Nesbitt][15] recommended the next two extensions. The first, [Drop Down Terminal][16], enables a terminal window to drop down from the top panel by pressing a certain key; the default is the key above Tab; on my keyboard, that's the tilde (~) character. Drop Down Terminal has a settings menu for customizing transparency, height, the activation keystroke, and other configurations.
|
||||
|
||||
## 10\. Todo.txt
|
||||
|
||||
[Todo.txt][17] adds a menu to the top panel for maintaining a file for Todo.txt task tracking. You can add or delete a task from the menu or mark it as completed.
|
||||
|
||||
![Drop-down menu for Todo.txt][18]
|
||||
|
||||
## 11\. Removable Drive Menu
|
||||
|
||||
Opensource.com editor [Seth Kenlon][19] suggested [Removable Drive Menu][20]. It provides a drop-down menu for managing removable media, such as USB thumb drives. From the extension's menu, you can access a drive's files and eject it. The menu only appears when removable media is inserted.
|
||||
|
||||
![Removable Drive Menu][21]
|
||||
|
||||
## 12\. GNOME Internet Radio
|
||||
|
||||
I enjoy listening to internet radio streams with the [GNOME Internet Radio][22] extension, which I wrote about in [How to Stream Music with GNOME Internet Radio][23].
|
||||
|
||||
* * *
|
||||
|
||||
What are your favorite GNOME extensions? Please share them in the comments.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/8/extensions-gnome-desktop
|
||||
|
||||
作者:[Alan Formy-Duval][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/alanfdosshttps://opensource.com/users/erezhttps://opensource.com/users/alanfdosshttps://opensource.com/users/patrickhttps://opensource.com/users/liamnairn
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/rh_003784_02_os.comcareers_os_rh2x.png?itok=jbRfXinl (A person working.)
|
||||
[2]: https://opensource.com/article/17/2/top-gnome-shell-extensions
|
||||
[3]: https://opensource.com/sites/default/files/uploads/add-onsextensions_6.png (Package Manager Add-ons Extensions view)
|
||||
[4]: https://wiki.gnome.org/Apps/Clocks
|
||||
[5]: https://wiki.gnome.org/Apps/Weather
|
||||
[6]: https://opensource.com/sites/default/files/uploads/clocksweatherdropdown_6.png (Clocks and Weather shown in the drop-down)
|
||||
[7]: https://extensions.gnome.org/extension/6/applications-menu/
|
||||
[8]: https://opensource.com/sites/default/files/uploads/add-onsextensionsettings_6.png (Extension Settings)
|
||||
[9]: https://opensource.com/sites/default/files/uploads/applicationsmenuextension_5.png (Applications Menu)
|
||||
[10]: https://extensions.gnome.org/extension/1305/more-columns-in-applications-view/
|
||||
[11]: https://extensions.gnome.org/extension/988/harddisk-led/
|
||||
[12]: https://extensions.gnome.org/extension/1381/load-average/
|
||||
[13]: https://extensions.gnome.org/extension/508/uptime-indicator/
|
||||
[14]: https://extensions.gnome.org/extension/906/sound-output-device-chooser/
|
||||
[15]: https://opensource.com/users/scottnesbitt
|
||||
[16]: https://extensions.gnome.org/extension/442/drop-down-terminal/
|
||||
[17]: https://extensions.gnome.org/extension/570/todotxt/
|
||||
[18]: https://opensource.com/sites/default/files/uploads/todo.txtmenu_3.png (Drop-down menu for Todo.txt)
|
||||
[19]: https://opensource.com/users/seth
|
||||
[20]: https://extensions.gnome.org/extension/7/removable-drive-menu/
|
||||
[21]: https://opensource.com/sites/default/files/uploads/removabledrivemenu_3.png (Removable Drive Menu)
|
||||
[22]: https://extensions.gnome.org/extension/836/internet-radio/
|
||||
[23]: https://opensource.com/article/19/6/gnome-internet-radio
|
||||
@@ -1,84 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (The infrastructure is code: A story of COBOL and Go)
|
||||
[#]: via: (https://opensource.com/article/19/8/command-line-heroes-cobol-golang)
|
||||
[#]: author: (Matthew Broberg https://opensource.com/users/mbbroberghttps://opensource.com/users/mbbroberghttps://opensource.com/users/mbbroberghttps://opensource.com/users/mbbroberghttps://opensource.com/users/mbbroberg)
|
||||
|
||||
The infrastructure is code: A story of COBOL and Go
|
||||
======
|
||||
COBOL remains the dominant language of mainframes. What can Go learn
|
||||
from its history to dominate the cloud?
|
||||
![Listen to the Command Line Heroes Podcast][1]
|
||||
|
||||
Old challenges are new again. In [this week's Command Line Heroes podcast][2] (Season 3, Episode 5), that thought comes with a twist of programming languages and platforms.
|
||||
|
||||
### COBOL dominates the mainframe
|
||||
|
||||
One of the most brilliant minds in all of computer science is [Grace Murray Hopper][3]. Every time we don't have to write in binary to talk to computers, I recommend saying out loud: "Thank you, Rear Admiral Grace Murray Hopper." Try it next time, for she is the one who invented the first compiler (the software that translates programming code to machine language).
|
||||
|
||||
Hopper was essential to the invention and adoption of high-level programming languages, the first of which was COBOL. She helped create the **CO**mmon **B**usiness-**O**riented **L**anguage (COBOL for short) in 1959. As Ritika Trikha put it on [HackerRank][4]:
|
||||
|
||||
> "Grace Hopper, the mother of COBOL, helped champion the creation of this brand-new programming language that aimed to function across all business systems, saving an immense amount of time and money. Hopper was also the first to believe that programming languages should read just like English instead of computer jargon. Hence why COBOL's syntax is so wordy. But it helped humanize the computing process for businesses during an era when computing was intensive and prevalent only in research facilities."
|
||||
|
||||
In the early 1960s, mainframes were a wild new architecture for sharing powerful amounts of computation. And in the era of mainframe computing, COBOL dominated the landscape.
|
||||
|
||||
### COBOL in today's world
|
||||
|
||||
But what about today? With the decline of mainframes and the rise of newer and more innovative languages designed for the web and cloud, where does COBOL sit?
|
||||
|
||||
As last week's episode of Command Line Heroes mentioned, in the late 1990s, [Perl][5] (as well as JavaScript and C++) was outpacing COBOL. And, as Perl's creator, [Larry Wall stated then][6]: "COBOL is no big deal these days since demand for COBOL seems to be trailing off, for some strange reason."
|
||||
|
||||
Fast forward to 2019, and COBOL has far from "trailed off." As David Cassel wrote on [The New Stack][7] in 2017:
|
||||
|
||||
> "About 95% of ATM swipes use COBOL code, Reuters [reported in April][8], and the 58-year-old language even powers 80% of in-person transactions. In fact, Reuters calculates that there's still 220 billion lines of COBOL code currently being used in production today, and that every day, COBOL systems handle $3 trillion in commerce."
|
||||
|
||||
Given its continued significance in the business world, knowing COBOL can be a great career move. Top COBOL programmers can expect to [make six figures][9] due to the limited number of people who specialize in the language.
|
||||
|
||||
### Go dominates in the cloud, for now
|
||||
|
||||
That story of COBOL's early dominance rings a bell for me. If we survey the most influential projects of this cloud computing era, you'd be hard-pressed to miss Go sitting at the top of the pack. Kubernetes and much of its related technology—from Etcd to Prometheus—are written in Go. As [RedMonk explored][10] back in 2014:
|
||||
|
||||
> "Go's rapidly closing in on 1% of total commits and half a percent of projects and contributors. While the trend is obviously interesting, at first glance, numbers well under one percent look inconsequential relative to overall adoption. To provide some context, however, each of the most popular languages on Ohloh (C, C++, Java, JavaScript) only constitute ~10% of commits and ~5% of projects and contributors. **That means Go, a seemingly very minor player, is already used nearly one-tenth as much in FOSS as the most popular languages in existence**."
|
||||
|
||||
In two of my previous jobs, my team (re)wrote infrastructure software in Go to be part of this monumental wave. Influential projects continue to live in the space that Go can fill, as [Uday Hiwarale explained][11] well in 2018:
|
||||
|
||||
> "Things that make Go a great language [are] its simple concurrency model, its package-based code management, and its non-strict (type inference) typing system. Go does not support out-of-the box object-oriented programming experience, but [its] support structures (structs) …, with the help of methods and pointers, can help us achieve the same [outcomes]."
|
||||
|
||||
It looks to me like Go could be following in COBOL's footsteps, but questions remain about where it's going. In June 2019, [RedMonk ranked][12] Go in 16th place, with a future that could lead either direction.
|
||||
|
||||
### What can Go learn from COBOL?
|
||||
|
||||
If Go were to see into its future, would it look like COBOL's, with such staying power?
|
||||
|
||||
The stories told this season by Command Line Heroes illustrate how languages are born, how communities form around them, how they rise in popularity and standardize, and how some slowly decline. What can we learn about the lifespan of programming languages? Do they have a similar arc? Or do they differ?
|
||||
|
||||
I think this podcast is well worth [subscribing so that you don't miss a single one][2]. I would love to hear your thoughts in the comments below.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/8/command-line-heroes-cobol-golang
|
||||
|
||||
作者:[Matthew Broberg][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/mbbroberghttps://opensource.com/users/mbbroberghttps://opensource.com/users/mbbroberghttps://opensource.com/users/mbbroberghttps://opensource.com/users/mbbroberg
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/command-line-heroes-520x292.png?itok=s_F6YEoS (Listen to the Command Line Heroes Podcast)
|
||||
[2]: https://www.redhat.com/en/command-line-heroes
|
||||
[3]: https://www.biography.com/scientist/grace-hopper
|
||||
[4]: https://blog.hackerrank.com/the-inevitable-return-of-cobol/
|
||||
[5]: https://opensource.com/article/19/8/command-line-heroes-perl
|
||||
[6]: http://www.wall.org/~larry/onion3/talk.html
|
||||
[7]: https://thenewstack.io/cobol-everywhere-will-maintain/
|
||||
[8]: http://fingfx.thomsonreuters.com/gfx/rngs/USA-BANKS-COBOL/010040KH18J/index.html
|
||||
[9]: https://www.laserfiche.com/ecmblog/looking-job-hows-your-cobol/
|
||||
[10]: https://redmonk.com/dberkholz/2014/03/18/go-the-emerging-language-of-cloud-infrastructure/
|
||||
[11]: https://medium.com/rungo/introduction-to-go-programming-language-golang-89d16ca72bbf
|
||||
[12]: https://redmonk.com/sogrady/2019/07/18/language-rankings-6-19/
|
||||
@@ -1,381 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (10 Ansible modules you need to know)
|
||||
[#]: via: (https://opensource.com/article/19/9/must-know-ansible-modules)
|
||||
[#]: author: (DirectedSoul https://opensource.com/users/directedsoulhttps://opensource.com/users/markphttps://opensource.com/users/rich-butkevichttps://opensource.com/users/jairojuniorhttps://opensource.com/users/marcobravohttps://opensource.com/users/johnsimcall)
|
||||
|
||||
10 Ansible modules you need to know
|
||||
======
|
||||
See examples and learn the most important modules for automating
|
||||
everyday tasks with Ansible.
|
||||
![Text editor on a browser, in blue][1]
|
||||
|
||||
[Ansible][2] is an open source IT configuration management and automation platform. It uses human-readable YAML templates so users can program repetitive tasks to happen automatically without having to learn an advanced programming language.
|
||||
|
||||
Ansible is agentless, which means the nodes it manages do not require any software to be installed on them. This eliminates potential security vulnerabilities and makes overall management smoother.
|
||||
|
||||
Ansible [modules][3] are standalone scripts that can be used inside an Ansible playbook. A playbook consists of a play, and a play consists of tasks. These concepts may seem confusing if you're new to Ansible, but as you begin writing and working more with playbooks, they will become familiar.
|
||||
|
||||
There are some modules that are frequently used in automating everyday tasks; those are the ones that we will cover in this article.
|
||||
|
||||
Ansible has three main files that you need to consider:
|
||||
|
||||
* **Host/inventory file:** Contains the entry of the nodes that need to be managed
|
||||
* **Ansible.cfg file:** Located by default at **/etc/ansible/ansible.cfg**, it has the necessary privilege escalation options and the location of the inventory file
|
||||
* **Main file:** A playbook that has modules that perform various tasks on a host listed in an inventory or host file
|
||||
|
||||
|
||||
|
||||
### Module 1: Package management
|
||||
|
||||
There is a module for most popular package managers, such as DNF and APT, to enable you to install any package on a system. Functionality depends entirely on the package manager, but usually these modules can install, upgrade, downgrade, remove, and list packages. The names of relevant modules are easy to guess. For example, the DNF module is [dnf_module][4], the old YUM module (required for Python 2 compatibility) is [yum_module][5], while the APT module is [apt_module][6], the Slackpkg module is [slackpkg_module][7], and so on.
|
||||
|
||||
Example 1:
|
||||
|
||||
|
||||
```
|
||||
\- name: install the latest version of Apache and MariaDB
|
||||
dnf:
|
||||
name:
|
||||
- httpd
|
||||
- mariadb-server
|
||||
state: latest
|
||||
```
|
||||
|
||||
This installs the Apache web server and the MariaDB SQL database.
|
||||
|
||||
#### Example 2:
|
||||
|
||||
|
||||
```
|
||||
\- name: Install a list of packages
|
||||
yum:
|
||||
name:
|
||||
- nginx
|
||||
- postgresql
|
||||
- postgresql-server
|
||||
state: present
|
||||
```
|
||||
|
||||
This installs the list of packages and helps download multiple packages.
|
||||
|
||||
### Module 2: Service
|
||||
|
||||
After installing a package, you need a module to start it. The [service module][8] enables you to start, stop, and reload installed packages; this comes in pretty handy.
|
||||
|
||||
#### Example 1:
|
||||
|
||||
|
||||
```
|
||||
\- name: Start service foo, based on running process /usr/bin/foo
|
||||
service:
|
||||
name: foo
|
||||
pattern: /usr/bin/foo
|
||||
state: started
|
||||
```
|
||||
|
||||
This starts the service **foo**.
|
||||
|
||||
#### Example 2:
|
||||
|
||||
|
||||
```
|
||||
\- name: Restart network service for interface eth0
|
||||
service:
|
||||
name: network
|
||||
state: restarted
|
||||
args: eth0
|
||||
```
|
||||
|
||||
This restarts the network service of the interface **eth0**.
|
||||
|
||||
### Module 3: Copy
|
||||
|
||||
The [copy module][9] copies a file from the local or remote machine to a location on the remote machine.
|
||||
|
||||
#### Example 1:
|
||||
|
||||
|
||||
```
|
||||
\- name: Copy a new "ntp.conf file into place, backing up the original if it differs from the copied version
|
||||
copy:
|
||||
src: /mine/ntp.conf
|
||||
dest: /etc/ntp.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
backup: yes
|
||||
```
|
||||
|
||||
#### Example 2:
|
||||
|
||||
|
||||
```
|
||||
\- name: Copy file with owner and permission, using symbolic representation
|
||||
copy:
|
||||
src: /srv/myfiles/foo.conf
|
||||
dest: /etc/foo.conf
|
||||
owner: foo
|
||||
group: foo
|
||||
mode: u=rw,g=r,o=r
|
||||
```
|
||||
|
||||
### Module 4: Debug
|
||||
|
||||
The [debug module][10] prints statements during execution and can be useful for debugging variables or expressions without having to halt the playbook.
|
||||
|
||||
#### Example 1:
|
||||
|
||||
|
||||
```
|
||||
\- name: Display all variables/facts known for a host
|
||||
debug:
|
||||
var: hostvars[inventory_hostname]
|
||||
verbosity: 4
|
||||
```
|
||||
|
||||
This displays all the variable information for a host that is defined in the inventory file.
|
||||
|
||||
#### Example 2:
|
||||
|
||||
|
||||
```
|
||||
\- name: Write some content in a file /tmp/foo.txt
|
||||
copy:
|
||||
dest: /tmp/foo.txt
|
||||
content: |
|
||||
Good Morning!
|
||||
Awesome sunshine today.
|
||||
register: display_file_content
|
||||
\- name: Debug display_file_content
|
||||
debug:
|
||||
var: display_file_content
|
||||
verbosity: 2
|
||||
```
|
||||
|
||||
This registers the content of the copy module output and displays it only when you specify verbosity as 2. For example:
|
||||
|
||||
|
||||
```
|
||||
`ansible-playbook demo.yaml -vv`
|
||||
```
|
||||
|
||||
### Module 5: File
|
||||
|
||||
The [file module][11] manages the file and its properties.
|
||||
|
||||
* It sets attributes of files, symlinks, or directories.
|
||||
* It also removes files, symlinks, or directories.
|
||||
|
||||
|
||||
|
||||
#### Example 1:
|
||||
|
||||
|
||||
```
|
||||
\- name: Change file ownership, group and permissions
|
||||
file:
|
||||
path: /etc/foo.conf
|
||||
owner: foo
|
||||
group: foo
|
||||
mode: '0644'
|
||||
```
|
||||
|
||||
This creates a file named **foo.conf** and sets the permission to **0644**.
|
||||
|
||||
#### Example 2:
|
||||
|
||||
|
||||
```
|
||||
\- name: Create a directory if it does not exist
|
||||
file:
|
||||
path: /etc/some_directory
|
||||
state: directory
|
||||
mode: '0755'
|
||||
```
|
||||
|
||||
This creates a directory named **some_directory** and sets the permission to **0755**.
|
||||
|
||||
### Module 6: Lineinfile
|
||||
|
||||
The [lineinfile module][12] manages lines in a text file.
|
||||
|
||||
* It ensures a particular line is in a file or replaces an existing line using a back-referenced regular expression.
|
||||
* It's primarily useful when you want to change just a single line in a file.
|
||||
|
||||
|
||||
|
||||
#### Example 1:
|
||||
|
||||
|
||||
```
|
||||
\- name: Ensure SELinux is set to enforcing mode
|
||||
lineinfile:
|
||||
path: /etc/selinux/config
|
||||
regexp: '^SELINUX='
|
||||
line: SELINUX=enforcing
|
||||
```
|
||||
|
||||
This sets the value of **SELINUX=enforcing**.
|
||||
|
||||
#### Example 2:
|
||||
|
||||
|
||||
```
|
||||
\- name: Add a line to a file if the file does not exist, without passing regexp
|
||||
lineinfile:
|
||||
path: /etc/resolv.conf
|
||||
line: 192.168.1.99 foo.lab.net foo
|
||||
create: yes
|
||||
```
|
||||
|
||||
This adds an entry for the IP and hostname in the **resolv.conf** file.
|
||||
|
||||
### Module 7: Git
|
||||
|
||||
The [git module][13] manages git checkouts of repositories to deploy files or software.
|
||||
|
||||
#### Example 1:
|
||||
|
||||
|
||||
```
|
||||
# Example Create git archive from repo
|
||||
\- git:
|
||||
repo: <https://github.com/ansible/ansible-examples.git>
|
||||
dest: /src/ansible-examples
|
||||
archive: /tmp/ansible-examples.zip
|
||||
```
|
||||
|
||||
#### Example 2:
|
||||
|
||||
|
||||
```
|
||||
\- git:
|
||||
repo: <https://github.com/ansible/ansible-examples.git>
|
||||
dest: /src/ansible-examples
|
||||
separate_git_dir: /src/ansible-examples.git
|
||||
```
|
||||
|
||||
This clones a repo with a separate Git directory.
|
||||
|
||||
### Module 8: Cli_command
|
||||
|
||||
The [cli_command module][14], first available in Ansible 2.7, provides a platform-agnostic way of pushing text-based configurations to network devices over the **network_cli connection** plugin.
|
||||
|
||||
#### Example 1:
|
||||
|
||||
|
||||
```
|
||||
\- name: commit with comment
|
||||
cli_config:
|
||||
config: set system host-name foo
|
||||
commit_comment: this is a test
|
||||
```
|
||||
|
||||
This sets the hostname for a switch and exits with a commit message.
|
||||
|
||||
#### Example 2:
|
||||
|
||||
|
||||
```
|
||||
\- name: configurable backup path
|
||||
cli_config:
|
||||
config: "{{ lookup('template', 'basic/config.j2') }}"
|
||||
backup: yes
|
||||
backup_options:
|
||||
filename: backup.cfg
|
||||
dir_path: /home/user
|
||||
```
|
||||
|
||||
This backs up a config to a different destination file.
|
||||
|
||||
### Module 9: Archive
|
||||
|
||||
The [archive module][15] creates a compressed archive of one or more files. By default, it assumes the compression source exists on the target.
|
||||
|
||||
#### Example 1:
|
||||
|
||||
|
||||
```
|
||||
\- name: Compress directory /path/to/foo/ into /path/to/foo.tgz
|
||||
archive:
|
||||
path: /path/to/foo
|
||||
dest: /path/to/foo.tgz
|
||||
```
|
||||
|
||||
#### Example 2:
|
||||
|
||||
|
||||
```
|
||||
\- name: Create a bz2 archive of multiple files, rooted at /path
|
||||
archive:
|
||||
path:
|
||||
- /path/to/foo
|
||||
- /path/wong/foo
|
||||
dest: /path/file.tar.bz2
|
||||
format: bz2
|
||||
```
|
||||
|
||||
### Module 10: Command
|
||||
|
||||
One of the most basic but useful modules, the [command module][16] takes the command name followed by a list of space-delimited arguments.
|
||||
|
||||
#### Example 1:
|
||||
|
||||
|
||||
```
|
||||
\- name: return motd to registered var
|
||||
command: cat /etc/motd
|
||||
register: mymotd
|
||||
```
|
||||
|
||||
#### Example 2:
|
||||
|
||||
|
||||
```
|
||||
\- name: Change the working directory to somedir/ and run the command as db_owner if /path/to/database does not exist.
|
||||
command: /usr/bin/make_database.sh db_user db_name
|
||||
become: yes
|
||||
become_user: db_owner
|
||||
args:
|
||||
chdir: somedir/
|
||||
creates: /path/to/database
|
||||
```
|
||||
|
||||
### Conclusion
|
||||
|
||||
There are tons of modules available in Ansible, but these ten are the most basic and powerful ones you can use for an automation job. As your requirements change, you can learn about other useful modules by entering **ansible-doc <module-name>** on the command line or refer to the [official documentation][17].
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/9/must-know-ansible-modules
|
||||
|
||||
作者:[DirectedSoul][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/directedsoulhttps://opensource.com/users/markphttps://opensource.com/users/rich-butkevichttps://opensource.com/users/jairojuniorhttps://opensource.com/users/marcobravohttps://opensource.com/users/johnsimcall
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/browser_blue_text_editor_web.png?itok=lcf-m6N7 (Text editor on a browser, in blue)
|
||||
[2]: https://www.ansible.com/
|
||||
[3]: https://docs.ansible.com/ansible/latest/user_guide/modules.html
|
||||
[4]: https://docs.ansible.com/ansible/latest/modules/dnf_module.html
|
||||
[5]: https://docs.ansible.com/ansible/latest/modules/yum_module.html
|
||||
[6]: https://docs.ansible.com/ansible/latest/modules/apt_module.html
|
||||
[7]: https://docs.ansible.com/ansible/latest/modules/slackpkg_module.html
|
||||
[8]: https://docs.ansible.com/ansible/latest/modules/service_module.html
|
||||
[9]: https://docs.ansible.com/ansible/latest/modules/copy_module.html
|
||||
[10]: https://docs.ansible.com/ansible/latest/modules/debug_module.html
|
||||
[11]: https://docs.ansible.com/ansible/latest/modules/file_module.html
|
||||
[12]: https://docs.ansible.com/ansible/latest/modules/lineinfile_module.html
|
||||
[13]: https://docs.ansible.com/ansible/latest/modules/git_module.html#git-module
|
||||
[14]: https://docs.ansible.com/ansible/latest/modules/cli_command_module.html
|
||||
[15]: https://docs.ansible.com/ansible/latest/modules/archive_module.html
|
||||
[16]: https://docs.ansible.com/ansible/latest/modules/command_module.html
|
||||
[17]: https://docs.ansible.com/
|
||||
@@ -1,320 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to build a Flatpak)
|
||||
[#]: via: (https://opensource.com/article/19/10/how-build-flatpak-packaging)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
How to build a Flatpak
|
||||
======
|
||||
A universal packaging format with a decentralized means of distribution.
|
||||
Plus, portability and sandboxing.
|
||||
![][1]
|
||||
|
||||
A long time ago, a Linux distribution shipped an operating system along with _all_ the software available for it. There was no concept of “third party” software because everything was a part of the distribution. Applications weren’t so much installed as they were enabled from a great big software repository that you got on one of the many floppy disks or, later, CDs you purchased or downloaded.
|
||||
|
||||
This evolved into something even more convenient as the internet became ubiquitous, and the concept of what is now the “app store” was born. Of course, Linux distributions tend to call this a _software repository_ or just _repo_ for short, with some variations for “branding”, such as _Ubuntu Software Center_ or, with typical GNOME minimalism, simply _Software_.
|
||||
|
||||
This model worked well back when open source software was still a novelty and the number of open source applications was a number rather than a _theoretical_ number. In today’s world of GitLab and GitHub and Bitbucket (and [many][2] [many][3] more), it’s hardly possible to count the number of open source projects, much less package them up in a repository. No Linux distribution today, even [Debian][4] and its formidable group of package maintainers, can claim or hope to have a package for every installable open source project.
|
||||
|
||||
Of course, a Linux package doesn’t have to be in a repository to be installable. Any programmer can package up their software and distribute it from their own website. However, because repositories are seen as an integral part of a distribution, there isn’t a universal packaging format, meaning that a programmer must decide whether to release a `.deb` or `.rpm`, or an AUR build script, or a Nix or Guix package, or a Homebrew script, or just a mostly-generic `.tgz` archive for `/opt`. It’s overwhelming for a developer who lives and breathes Linux every day, much less for a developer just trying to make a best-effort attempt at supporting a free and open source target.
|
||||
|
||||
### Why Flatpak?
|
||||
|
||||
The Flatpak project provides a universal packaging format along with a decentralized means of distribution, plus portability, and sandboxing.
|
||||
|
||||
* **Universal** Install the Flatpak system, and you can run Flatpaks, regardless of your distribution. No daemon or systemd required. The same Flatpak runs on Fedora, Ubuntu, Mageia, Pop OS, Arch, Slackware, and more.
|
||||
* **Decentralized** Developers can create and sign their own Flatpak packages and repositories. There’s no repository to petition in order to get a package included.
|
||||
* **Portability** If you have a Flatpak on your system and want to hand it to a friend so they can run the same application, you can export the Flatpak to a USB thumbdrive.
|
||||
* **Sandboxed** Flatpaks use a container-based model, allowing multiple versions of libraries and applications to exist on one system. Yes, you can easily install the latest version of an app to test out while maintaining the old version you rely on.
|
||||
|
||||
|
||||
|
||||
### Building a Flatpak
|
||||
|
||||
To build a Flatpak, you must first install Flatpak (the subsystem that enables you to use Flatpak packages) and the Flatpak-builder application.
|
||||
|
||||
On Fedora, CentOS, RHEL, and similar:
|
||||
|
||||
|
||||
```
|
||||
`$ sudo dnf install flatpak flatpak-builder`
|
||||
```
|
||||
|
||||
On Debian, Ubuntu, and similar:
|
||||
|
||||
|
||||
```
|
||||
`$ sudo apt install flatpak flatpak-builder`
|
||||
```
|
||||
|
||||
You must also install the development tools required to build the application you are packaging. By nature of developing the application you’re now packaging, you may already have a development environment installed, so you might not notice that these components are required, but should you start building Flatpaks with Jenkins or from inside containers, then you must ensure that your build tools are a part of your toolchain.
|
||||
|
||||
For the first example build, this article assumes that your application uses [GNU Autotools][5], but Flatpak itself supports other build systems, such as `cmake`, `cmake-ninja`, `meson`, `ant`, as well as custom commands (a `simple` build system, in Flatpak terminology, but by no means does this imply that the build itself is actually simple).
|
||||
|
||||
#### Project directory
|
||||
|
||||
Unlike the strict RPM build infrastructure, Flatpak doesn’t impose a project directory structure. I prefer to create project directories based on the **dist** packages of software, but there’s no technical reason you can’t instead integrate your Flatpak build process with your source directory. It is technically easier to build a Flatpak from your **dist** package, though, and it’s an easier demo too, so that’s the model this article uses. Set up a project directory for GNU Hello, serving as your first Flatpak:
|
||||
|
||||
|
||||
```
|
||||
$ mkdir hello_flatpak
|
||||
$ mkdir src
|
||||
```
|
||||
|
||||
Download your distributable source. For this example, the source code is located at `https://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz`.
|
||||
|
||||
|
||||
```
|
||||
$ cd hello_flatpak
|
||||
$ wget <https://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz>
|
||||
```
|
||||
|
||||
#### Manifest
|
||||
|
||||
A Flatpak is defined by a manifest, which describes how to build and install the application it is delivering. A manifest is atomic and reproducible. A Flatpak exists in a “sandbox” container, though, so the manifest is based on a mostly empty environment with a root directory call `/app`.
|
||||
|
||||
The first two attributes are the ID of the application you are packaging and the command provided by it. The application ID must be unique to the application you are packaging. The canonical way of formulating a unique ID is to use a triplet value consisting of the entity responsible for the code followed by the name of the application, such as `org.gnu.Hello`. The command provided by the application is whatever you type into a terminal to run the application. This does not imply that the application is intended to be run from a terminal instead of a `.desktop` file in the Activities or Applications menu.
|
||||
|
||||
In a file called `org.gnu.Hello.yaml`, enter this text:
|
||||
|
||||
|
||||
```
|
||||
id: org.gnu.Hello
|
||||
command: hello
|
||||
```
|
||||
|
||||
A manifest can be written in [YAML][6] or in JSON. This article uses YAML.
|
||||
|
||||
Next, you must define each “module” delivered by this Flatpak package. You can think of a module as a dependency or a component. For GNU Hello, there is only one module: GNU Hello. More complex applications may require a specific library or another application entirely.
|
||||
|
||||
|
||||
```
|
||||
modules:
|
||||
- name: hello
|
||||
buildsystem: autotools
|
||||
no-autogen: true
|
||||
sources:
|
||||
- type: archive
|
||||
path: src/hello-2.10.tar.gz
|
||||
```
|
||||
|
||||
The `buildsystem` value identifies how Flatpak must build the module. Each module can use its own build system, so one Flatpak can have several build systems defined.
|
||||
|
||||
The `no-autogen` value tells Flatpak not to run the setup commands for `autotools`, which aren’t necessary because the GNU Hello source code is the product of `make dist`. If the code you’re building isn’t in a easily buildable form, then you may need to install `autogen` and `autoconf` to prepare the source for `autotools`. This option doesn’t apply at all to projects that don’t use `autotools`.
|
||||
|
||||
The `type` value tells Flatpak that the source code is in an archive, which triggers the requisite unarchival tasks before building. The `path` points to the source code. In this example, the source exists in the `src` directory on your local build machine, but you could instead define the source as a remote location:
|
||||
|
||||
|
||||
```
|
||||
modules:
|
||||
- name: hello
|
||||
buildsystem: autotools
|
||||
no-autogen: true
|
||||
sources:
|
||||
- type: archive
|
||||
url: <https://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz>
|
||||
```
|
||||
|
||||
Finally, you must define the platform required for the application to run and build. The Flatpak maintainers supply runtimes and SDKs that include common libraries, including `freedesktop`, `gnome`, and `kde`. The basic requirement is the `freedesk` runtime and SDK, although this may be superseded by GNOME or KDE, depending on what your code needs to run. For this GNU Hello example, only the basics are required.
|
||||
|
||||
|
||||
```
|
||||
runtime: org.freedesktop.Platform
|
||||
runtime-version: '18.08'
|
||||
sdk: org.freedesktop.Sdk
|
||||
```
|
||||
|
||||
The entire GNU Hello flatpak manifest:
|
||||
|
||||
|
||||
```
|
||||
id: org.gnu.Hello
|
||||
runtime: org.freedesktop.Platform
|
||||
runtime-version: '18.08'
|
||||
sdk: org.freedesktop.Sdk
|
||||
command: hello
|
||||
modules:
|
||||
- name: hello
|
||||
buildsystem: autotools
|
||||
no-autogen: true
|
||||
sources:
|
||||
- type: archive
|
||||
path: src/hello-2.10.tar.gz
|
||||
```
|
||||
|
||||
#### Building a Flatpak
|
||||
|
||||
Now that the package is defined, you can build it. The build process prompts Flatpak-builder to parse the manifest and to resolve each requirement: it ensures that the necessary Platform and SDK are available (if they aren’t, then you’ll have to install them with the `flatpak` command), it unarchives the source code, and executes the `buildsystem` specified.
|
||||
|
||||
The command to start:
|
||||
|
||||
|
||||
```
|
||||
`$ flatpak-builder build-dir org.gnu.Hello.yaml`
|
||||
```
|
||||
|
||||
The directory `build-dir` is created if it does not already exist. The name `build-dir` is arbitrary; you could call it `build` or `bld` or `penguin`, and you can have more than one build destination in the same project directory. However, the term `build-dir` is a frequent value used in documentation, so using it as the literal value can be helpful.
|
||||
|
||||
#### Testing your application
|
||||
|
||||
You can test your application before or after it has been built by running the build command along with the `--run` option, and endingi the command with the command provided by the Flatpak:
|
||||
|
||||
|
||||
```
|
||||
$ flatpak-builder --run build-dir \
|
||||
org.gnu.Hello.yaml hello
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
### Packaging GUI apps with Flatpak
|
||||
|
||||
Packaging up a simple self-contained _hello world_ application is trivial, and fortunately packaging up a GUI application isn’t much harder. The most difficult applications to package are those that don’t rely on common libraries and frameworks (in the context of packaging, “common” means anything _not_ already packaged by someone else). The Flatpak community provides SDKs and SDK Extensions for many components you might otherwise have had to package yourself. For instance, when packaging the pure Java implementation of `pdftk`, I use the OpenJDK SDK extension I found in the Flatpak Github repository:
|
||||
|
||||
|
||||
```
|
||||
runtime: org.freedesktop.Platform
|
||||
runtime-version: '18.08'
|
||||
sdk: org.freedesktop.Sdk
|
||||
sdk-extensions:
|
||||
- org.freedesktop.Sdk.Extension.openjdk11
|
||||
```
|
||||
|
||||
The Flatpak community does a lot of work on the foundations required for applications to run upon in order to make the packaging process easy for developers. For instance, the Kblocks game from the KDE community requires the KDE platform to run, and that’s already available from Flatpak. The additional `libkdegames` library is not included, but it’s as easy to add it to your list of `modules` as `kblocks` itself.
|
||||
|
||||
Here’s a manifest for the Kblocks game:
|
||||
|
||||
|
||||
```
|
||||
id: org.kde.kblocks
|
||||
command: kblocks
|
||||
modules:
|
||||
\- buildsystem: cmake-ninja
|
||||
name: libkdegames
|
||||
sources:
|
||||
type: archive
|
||||
path: src/libkdegames-19.08.2.tar.xz
|
||||
\- buildsystem: cmake-ninja
|
||||
name: kblocks
|
||||
sources:
|
||||
type: archive
|
||||
path: src/kblocks-19.08.2.tar.xz
|
||||
runtime: org.kde.Platform
|
||||
runtime-version: '5.13'
|
||||
sdk: org.kde.Sdk
|
||||
```
|
||||
|
||||
As you can see, the manifest is still straight-forward and relatively intuitive. The build system is different, and the runtime and SDK point to KDE instead of the Freedesktop, but the structure and requirements are basically the same.
|
||||
|
||||
Because it’s a GUI application, however, there are some new options required. First, it needs an icon so that when it’s listed in the Activities or Application menu, it looks nice and recognizable. Kblocks includes an icon in its sources, but the names of files exported by a Flatpak must be prefixed using the application ID (such as `org.kde.Kblocks.desktop`). The easiest way to do this is to rename the file directly in the application source, which Flatpak can do for you as long as you include this directive in your manifest:
|
||||
|
||||
|
||||
```
|
||||
`rename-icon: kblocks`
|
||||
```
|
||||
|
||||
Another unique trait of GUI applications is that they often require integration with common desktop services, like the graphics server (X11 or Wayland) itself, a sound server such as [Pulse Audio][7], and the Inter-Process Communication (IPC) subsystem.
|
||||
|
||||
In the case of Kblocks, the requirements are:
|
||||
|
||||
|
||||
```
|
||||
finish-args:
|
||||
\- --share=ipc
|
||||
\- --socket=x11
|
||||
\- --socket=wayland
|
||||
\- --socket=pulseaudio
|
||||
\- --device=dri
|
||||
\- --filesystem=xdg-config/kdeglobals:ro
|
||||
```
|
||||
|
||||
Here’s the final, complete manifest, using URLs for the sources so you can try this on your own system easily:
|
||||
|
||||
|
||||
```
|
||||
command: kblocks
|
||||
finish-args:
|
||||
\- --share=ipc
|
||||
\- --socket=x11
|
||||
\- --socket=wayland
|
||||
\- --socket=pulseaudio
|
||||
\- --device=dri
|
||||
\- --filesystem=xdg-config/kdeglobals:ro
|
||||
id: org.kde.kblocks
|
||||
modules:
|
||||
\- buildsystem: cmake-ninja
|
||||
name: libkdegames
|
||||
sources:
|
||||
- sha256: 83456cec44502a1f79c0be00c983090e32fd8aea5fec1461fbfbd37b5f8866ac
|
||||
type: archive
|
||||
url: <https://download.kde.org/stable/applications/19.08.2/src/libkdegames-19.08.2.tar.xz>
|
||||
\- buildsystem: cmake-ninja
|
||||
name: kblocks
|
||||
sources:
|
||||
- sha256: 8b52c949e2d446a4ccf81b09818fc90234f2f55d8722c385491ee67e1f2abf93
|
||||
type: archive
|
||||
url: <https://download.kde.org/stable/applications/19.08.2/src/kblocks-19.08.2.tar.xz>
|
||||
rename-icon: kblocks
|
||||
runtime: org.kde.Platform
|
||||
runtime-version: '5.13'
|
||||
sdk: org.kde.Sdk
|
||||
```
|
||||
|
||||
To build the application, you must have the KDE Platform and SDK Flatpaks (version 5.13 as of this writing) installed. Once the application has been built, you can run it using the `--run` method, but to see the application icon, you must install it.
|
||||
|
||||
#### Distributing and installing a Flatpak you have built
|
||||
|
||||
Distributing flatpaks happen through repositories.
|
||||
|
||||
You can list your apps on [Flathub.org][8], a community website meant as a _technically_ decentralised (but central in spirit) location for Flatpaks. To submit your Flatpak, [place your manifest into a Git repository][9] and [submit a pull request on Github][10].
|
||||
|
||||
Alternately, you can create your own repository using the `flatpak build-export` command.
|
||||
|
||||
You can also just install locally:
|
||||
|
||||
|
||||
```
|
||||
`$ flatpak-builder --force-clean --install build-dir org.kde.Kblocks.yaml`
|
||||
```
|
||||
|
||||
Once installed, open your Activities or Applications menu and search for Kblocks.
|
||||
|
||||
![The Activities menu in GNOME][11]
|
||||
|
||||
### Learning more
|
||||
|
||||
The [Flatpak documentation site][12] has a good walkthrough on building your first Flatpak. It’s worth reading even if you’ve followed along with this article. Besides that, the docs provide details on what Platforms and SDKs are available.
|
||||
|
||||
For those who enjoy learning from examples, there are manifests for _every application_ available on [Flathub][13].
|
||||
|
||||
The resources to build and use Flatpaks are plentiful, and Flatpak, along with containers and sandboxed apps, are arguably [the future][14], so get familiar with them, start integrating them with your Jenkins pipelines, and enjoy easy and universal Linux app packaging.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/10/how-build-flatpak-packaging
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/flatpak-lead-image.png?itok=J93RG_fi
|
||||
[2]: http://notabug.org
|
||||
[3]: http://savannah.nongnu.org/
|
||||
[4]: http://debian.org
|
||||
[5]: https://opensource.com/article/19/7/introduction-gnu-autotools
|
||||
[6]: https://www.redhat.com/sysadmin/yaml-tips
|
||||
[7]: https://opensource.com/article/17/1/linux-plays-sound
|
||||
[8]: http://flathub.org
|
||||
[9]: https://opensource.com/resources/what-is-git
|
||||
[10]: https://opensource.com/life/16/3/submit-github-pull-request
|
||||
[11]: https://opensource.com/sites/default/files/gnome-activities-kblocks.jpg (The Activities menu in GNOME)
|
||||
[12]: http://docs.flatpak.org/en/latest/introduction.html
|
||||
[13]: https://github.com/flathub
|
||||
[14]: https://silverblue.fedoraproject.org/
|
||||
@@ -1,140 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (A guide to intermediate awk scripting)
|
||||
[#]: via: (https://opensource.com/article/19/11/intermediate-awk-scripting)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
A guide to intermediate awk scripting
|
||||
======
|
||||
Learn how to structure commands into executable scripts.
|
||||
![Coding on a computer][1]
|
||||
|
||||
This article explores awk's capabilities, which are easier to use now that you know how to structure your command into an executable script.
|
||||
|
||||
### Logical operators and conditionals
|
||||
|
||||
You can use the logical operators **and** (written **&&**) and **or** (written **||**) to add specificity to your conditionals.
|
||||
|
||||
For example, to select and print only records with the string "purple" in the second column _and_ an amount less than five in the third column:
|
||||
|
||||
|
||||
```
|
||||
`$2 == "purple" && $3 < 5 {print $1}`
|
||||
```
|
||||
|
||||
If a record has "purple" in column two but a value greater than five in column three, then it is _not_ selected. Similarly, if a record matches column three's requirement but lacks "purple" in column two, it is also _not_ selected.
|
||||
|
||||
### Next command
|
||||
|
||||
Say you want to select every record in your file where the amount is greater than or equal to eight and print a matching record with two asterisks (******). You also want to flag every record with a value between five (inclusive) and eight with only one asterisk (*****). There are a few ways to do this, and one way is to use the **next** command to instruct awk that after it takes an action, it should stop scanning and proceed to the _next_ record.
|
||||
|
||||
Here's an example:
|
||||
|
||||
|
||||
```
|
||||
NR == 1 {
|
||||
print $0;
|
||||
next;
|
||||
}
|
||||
|
||||
$3 >= 8 {
|
||||
printf "%s\t%s\n", $0, "**";
|
||||
next;
|
||||
}
|
||||
|
||||
$3 >= 5 {
|
||||
printf "%s\t%s\n", $0, "*";
|
||||
next;
|
||||
}
|
||||
|
||||
$3 < 5 {
|
||||
print $0;
|
||||
}
|
||||
```
|
||||
|
||||
### BEGIN command
|
||||
|
||||
The **BEGIN** command lets you print and set variables before awk starts scanning a text file. For instance, you can set the input and output field separators inside your awk script by defining them in a **BEGIN** statement. This example adapts the simple script from the previous article for a file with fields delimited by commas instead of whitespace:
|
||||
|
||||
|
||||
```
|
||||
#!/usr/bin/awk -f
|
||||
#
|
||||
# Print each record EXCEPT
|
||||
# IF the first record contains "raspberry",
|
||||
# THEN replace "red" with "pi"
|
||||
|
||||
BEGIN {
|
||||
FS=",";
|
||||
}
|
||||
|
||||
$1 == "raspberry" {
|
||||
gsub(/red/,"pi")
|
||||
}
|
||||
```
|
||||
|
||||
### END command
|
||||
|
||||
The **END** command, like **BEGIN**, allows you to perform actions in awk after it completes its scan through the text file you are processing. If you want to print cumulative results of some value in all records, you can do that only after all records have been scanned and processed.
|
||||
|
||||
The **BEGIN** and **END** commands run only once each. All rules between them run zero or more times on _each record_. In other words, most of your awk script is a loop that is executed at every new line of the text file you're processing, with the exception of the **BEGIN** and **END** rules, which run before and after the loop.
|
||||
|
||||
Here is an example that wouldn't be possible without the **END** command. This script accepts values from the output of the **df** Unix command and increments two custom variables (**used** and **available**) with each new record.
|
||||
|
||||
|
||||
```
|
||||
$1 != "tempfs" {
|
||||
used += $3;
|
||||
available += $4;
|
||||
}
|
||||
|
||||
END {
|
||||
printf "%d GiB used\n%d GiB available\n", used/2^20, available/2^20;
|
||||
}
|
||||
```
|
||||
|
||||
Save the script as **total.awk** and try it:
|
||||
|
||||
|
||||
```
|
||||
`df -l | awk -f total.awk`
|
||||
```
|
||||
|
||||
The **used** and **available** variables act like variables in many other programming languages. You create them arbitrarily and without declaring their type, and you add values to them at will. At the end of the loop, the script adds the records in the respective columns together and prints the totals.
|
||||
|
||||
### Math
|
||||
|
||||
As you can probably tell from all the logical operators and casual calculations so far, awk does math quite naturally. This arguably makes it a very useful calculator for your terminal. Instead of struggling to remember the rather unusual syntax of **bc**, you can just use awk along with its special **BEGIN** function to avoid the requirement of a file argument:
|
||||
|
||||
|
||||
```
|
||||
$ awk 'BEGIN { print 2*21 }'
|
||||
42
|
||||
$ awk 'BEGIN {print 8*log(4) }'
|
||||
11.0904
|
||||
```
|
||||
|
||||
Admittedly, that's still a lot of typing for simple (and not so simple) math, but it wouldn't take much effort to write a frontend, which is an exercise for you to explore.
|
||||
|
||||
* * *
|
||||
|
||||
_This article is adapted from an episode of [Hacker Public Radio][2], a community technology podcast._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/11/intermediate-awk-scripting
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/lnrCoder)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/code_computer_laptop_hack_work.png?itok=aSpcWkcl (Coding on a computer)
|
||||
[2]: http://hackerpublicradio.org/eps.php?id=2129
|
||||
@@ -1,853 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (5 open source plugins for Flutter apps)
|
||||
[#]: via: (https://opensource.com/article/19/11/open-source-plugins-flutter-apps)
|
||||
[#]: author: (Baradwaj Varadharajan https://opensource.com/users/baradwaj)
|
||||
|
||||
5 open source plugins for Flutter apps
|
||||
======
|
||||
Create more useful and attractive apps faster with these plugins for
|
||||
Google's cross-platform development language.
|
||||
![][1]
|
||||
|
||||
[Flutter][2] is the newest addition to Google's programming cadre. Following the success of Android, Kotlin, and Golang, [Flutter][3] was created as a cross-platform application development language. It is primarily based on the Dart programming construct and is considered to be the next big programming paradigm because its code can run as a mobile app, a web app, and even a desktop app without any major changes. Supposedly it will support Google's upcoming [Fuschia][4] operating system.
|
||||
|
||||
Flutter plugins are simple dependencies that extend the language's capabilities. This list of the top five open source Flutter plugins includes both user interface (UI)-related and function-related plugins.
|
||||
|
||||
The plugins must be included in your **pubspec.yaml** file before they can be used; they are required to make modifications to the **pubspec.yaml** file in the **lib** folder inside the project.
|
||||
|
||||
### Flutter video-player plugin
|
||||
|
||||
The video_player plugin allows you to embed videos to play in Flutter apps.
|
||||
|
||||
_Note: Up to Flutter 1.9, there is no video player support present in Flutter, so you have to depend on external plugins like video_player. This provides us with the VideoPlayer class which we will be using here._
|
||||
|
||||
Before using the VideoPlayer class in Flutter, you have to do the following for iOS and Android applications.
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
**For Android:**
|
||||
|
||||
Make sure that the minimum SDK is set to 21. You can modify this through the Build Gradle inside the **android>app** folder.
|
||||
|
||||
Next, make sure that the **AndroidManifest.xml** file has internet permission enabled by adding the following line in the **AndroidManifest.xml** file:
|
||||
|
||||
|
||||
```
|
||||
`<uses-permission android:name="android.permission.INTERNET"/>`
|
||||
```
|
||||
|
||||
**For iOS:**
|
||||
|
||||
To give permission to use the internet to render the videos (if required), add the following lines to the **info.plist** file in **<project root>/ios/Runner/Info.plist**:
|
||||
|
||||
|
||||
```
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
```
|
||||
|
||||
Once the prerequisites for Android and iOS are done, add the following line to the **pubspec.yaml** file in the **dependency** section:
|
||||
|
||||
|
||||
```
|
||||
`dependencies: video_player: ^0.10.1+3`
|
||||
```
|
||||
|
||||
Then use **get packages** to sync the project.
|
||||
|
||||
#### Video_Player plugin basics
|
||||
|
||||
The Video_Player plugin provides support for playing network and local videos on a device by creating a simple API to call the videos. The example application below shows how the Video_Player plugin uses the controller object and how to create the **Future<> Builder** to play the video on loading.
|
||||
|
||||
Start by using the [Scaffold widget][5] as the parent body widget. The goal is for the user to be able to play and pause a video using button controls.
|
||||
|
||||
In order to call the video player object from anywhere, you need a controller to hold it. The VideoPlayerController class makes this possible. The example application pieces it all together.
|
||||
|
||||
#### Example Flutter Video Player app
|
||||
|
||||
|
||||
```
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
void main() => runApp(VideoPlayerApp());
|
||||
|
||||
class VideoPlayerApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Video Player Demo',
|
||||
home: VideoPlayerScreen(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class VideoPlayerScreen extends StatefulWidget {
|
||||
VideoPlayerScreen({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_VideoPlayerScreenState createState() => _VideoPlayerScreenState();
|
||||
}
|
||||
|
||||
class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
|
||||
VideoPlayerController _controller;
|
||||
Future<void> _initializeVideoPlayerFuture;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_controller = VideoPlayerController.network(
|
||||
'<https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4>',
|
||||
);
|
||||
|
||||
// Initialize the controller and store the Future for later use.
|
||||
_initializeVideoPlayerFuture = _controller.initialize();
|
||||
|
||||
// Use the controller to loop the video.
|
||||
_controller.setLooping(true);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Ensure disposing of the VideoPlayerController to free up resources.
|
||||
_controller.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
drawer: Drawer(),
|
||||
backgroundColor: Colors.orangeAccent,
|
||||
appBar: AppBar(
|
||||
title: Text('Bee Video'),
|
||||
backgroundColor: Colors.black87,
|
||||
),
|
||||
// Use a FutureBuilder to display a loading spinner while waiting for the
|
||||
// VideoPlayerController to finish initializing.
|
||||
body: Stack(
|
||||
children: <Widget>[
|
||||
Center(child:FutureBuilder(
|
||||
future: _initializeVideoPlayerFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
// If the VideoPlayerController has finished initialization, use
|
||||
// the data it provides to limit the aspect ratio of the video.
|
||||
return AspectRatio(
|
||||
aspectRatio: _controller.value.aspectRatio,
|
||||
// Use the VideoPlayer widget to display the video.
|
||||
child: VideoPlayer(_controller),
|
||||
);
|
||||
} else {
|
||||
// If the VideoPlayerController is still initializing, show a
|
||||
// loading spinner.
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
)),
|
||||
Center(
|
||||
child:
|
||||
ButtonTheme(
|
||||
height: 100.0,
|
||||
minWidth: 200.0,
|
||||
child: RaisedButton(
|
||||
padding: EdgeInsets.all(60.0),
|
||||
color: Colors.transparent,
|
||||
textColor: Colors.white,
|
||||
onPressed: () {
|
||||
// Wrap the play or pause in a call to `setState`. This ensures the
|
||||
// correct icon is shown.
|
||||
setState(() {
|
||||
// If the video is playing, pause it.
|
||||
if (_controller.value.isPlaying) {
|
||||
_controller.pause();
|
||||
} else {
|
||||
// If the video is paused, play it.
|
||||
_controller.play();
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Icon(
|
||||
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
|
||||
size: 120.0,
|
||||
),
|
||||
))
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's the resulting application.
|
||||
|
||||
![Flutter Video Plugin][6]
|
||||
|
||||
![Flutter Video Plugin][7]
|
||||
|
||||
### Flutter Shimmer Effect plugin
|
||||
|
||||
The Flutter [Shimmer Effect][8] UI plugin is fairly straightforward: You just make use of only one class in your widget tree, and the work is done. Here is what the Shimmer Effect looks like:
|
||||
|
||||
![Flutter Shimmer Effect plugin][9]
|
||||
|
||||
To implement this effect, jump into the widget definition and use the **Shimmer** class in your widget tree with this option:
|
||||
|
||||
|
||||
```
|
||||
`Shimmer.fromColors`
|
||||
```
|
||||
|
||||
Next, finalize the application by filling out the properties described below.
|
||||
|
||||
#### Shimmer.fromColors properties
|
||||
|
||||
**Shimmer.fromColors** has the following properties:
|
||||
|
||||
* **baseColor:** This is the shimmer's base color that gets shown on the widget. This is the primary color and the one the child widget will use.
|
||||
* **HighlightColor:** This is the color that produces the shimmer-like effect by continually waving across the child widget.
|
||||
* **Child:** This holds whatever widget produces the Shimmer Effect. It could be a Text widget or any complex structure.
|
||||
|
||||
|
||||
|
||||
The example program shows how these attributes work across complex widgets.
|
||||
|
||||
#### Example Flutter Shimmer Effect app
|
||||
|
||||
This example produces the Shimmer Effect for two important widgets: the Text widget and the [Listview widget][10].
|
||||
|
||||
|
||||
```
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
|
||||
void main() => runApp(ShimmerEffectApp());
|
||||
|
||||
class ShimmerEffectApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Sample ShimmerEffect Widget',
|
||||
home: ShimmerWidget(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ShimmerWidget extends StatefulWidget {
|
||||
ShimmerWidget({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ShimmerWidgetState createState() => _ShimmerWidgetState();
|
||||
}
|
||||
|
||||
class _ShimmerWidgetState extends State {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text("Shimmer effect"),),
|
||||
body: Container(
|
||||
padding: EdgeInsets.all(25.0),
|
||||
child:Center(
|
||||
child: Shimmer.fromColors(
|
||||
direction: ShimmerDirection.rtl,
|
||||
period: Duration(seconds:5),
|
||||
child: Column(
|
||||
children: [0, 1, 2, 3]
|
||||
.map((_) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 48.0,
|
||||
height: 48.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 8.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 2.0),
|
||||
),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 8.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 2.0),
|
||||
),
|
||||
Container(
|
||||
width: 40.0,
|
||||
height: 8.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
baseColor: Colors.grey[700],
|
||||
highlightColor: Colors.grey[100]),
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's the resulting application.
|
||||
|
||||
![Flutter Shimmer Effect plugin][11]
|
||||
|
||||
![Flutter Shimmer Effect plugin][12]
|
||||
|
||||
### Flutter Badges plugin
|
||||
|
||||
[Flutter Badges][13] is a very useful UI plugin that marks a notification count, a count of items in an e-commerce basket, etc.
|
||||
|
||||
To use the Flutter Badges plugin, add the following dependency in your **pubspec.yaml** file:
|
||||
|
||||
|
||||
```
|
||||
dependencies:
|
||||
badges: ^1.1.0
|
||||
```
|
||||
|
||||
Then, import the following line into your **main.dart** file:
|
||||
|
||||
|
||||
```
|
||||
`import 'package:badges/badges.dart';`
|
||||
```
|
||||
|
||||
Now, you can create badges with a simple call to the **Badge class.**
|
||||
|
||||
#### Badge class properties
|
||||
|
||||
Following are the Badge class's available properties:
|
||||
|
||||
* **badgeContent:** This is the attribute that takes in the value of the Badge. It could be a number, a letter, etc. Make sure to make it as small as possible!
|
||||
* **BadgeColor:** Control the color of the badge by adjusting the BadgeColor colors property.
|
||||
* **AnimationType:** This enables three animations for the Badge:
|
||||
* **BadgeAnimationType.scale:** Scales animation once loading happens.
|
||||
* **BadgeAnimationType.fade:** Fades animation once loading happens
|
||||
* **BadgeAnimationType.slide:** Slides animation once loading happens
|
||||
* **shape:** This controls the shape of the badge; it could be a circle or a square.
|
||||
* **AnimationDuration:** This takes in a Duration class as its value to set how long the animation should last.
|
||||
|
||||
|
||||
|
||||
Once these attributes are set, you can create a simple app like the following.
|
||||
|
||||
#### Example Flutter Badge application
|
||||
|
||||
Add the following to your **main.dart** file and run the application.
|
||||
|
||||
|
||||
```
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:badges/badges.dart';
|
||||
|
||||
void main() => runApp(BadgesApp());
|
||||
|
||||
class BadgesApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new MaterialApp(
|
||||
title: 'APP',
|
||||
home: BadgesWidget(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BadgesWidget extends StatefulWidget {
|
||||
BadgesWidget({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_BadgesWidgetState createState() => _BadgesWidgetState();
|
||||
}
|
||||
|
||||
class _BadgesWidgetState extends State {
|
||||
|
||||
int value = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title:Text("Sample Badges")),
|
||||
body: Center(
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(25.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Spacer(),
|
||||
Badge(
|
||||
child: RaisedButton(
|
||||
color: Colors.blueGrey,
|
||||
child: Text("Notifications", style: TextStyle(color: Colors.white),),
|
||||
onPressed: (){
|
||||
setState(() {
|
||||
value = value + 1;
|
||||
});
|
||||
},),
|
||||
badgeContent: Text('$value',style: TextStyle(color: Colors.white),),
|
||||
badgeColor: Colors.red,
|
||||
animationType: BadgeAnimationType.scale,
|
||||
animationDuration: Duration(milliseconds: 500),
|
||||
shape: BadgeShape.circle,
|
||||
),
|
||||
Spacer(),
|
||||
Badge(
|
||||
child: RaisedButton(
|
||||
color: Colors.blueGrey,
|
||||
child: Text("Messages", style: TextStyle(color: Colors.white),),
|
||||
onPressed: (){
|
||||
|
||||
},),
|
||||
badgeContent: Text("2",style: TextStyle(color: Colors.white),),
|
||||
badgeColor: Colors.red,
|
||||
animationType: BadgeAnimationType.scale,
|
||||
animationDuration: Duration(seconds: 1),
|
||||
shape: BadgeShape.circle,
|
||||
),
|
||||
Spacer(),
|
||||
Badge(
|
||||
child: RaisedButton(
|
||||
color: Colors.blueGrey,
|
||||
child: Text("Notifications", style: TextStyle(color: Colors.white),),
|
||||
onPressed: (){
|
||||
},),
|
||||
badgeContent: Text("2",style: TextStyle(color: Colors.white),),
|
||||
badgeColor: Colors.red,
|
||||
animationType: BadgeAnimationType.scale,
|
||||
animationDuration: Duration(seconds: 1),
|
||||
shape: BadgeShape.circle,
|
||||
),
|
||||
Spacer(flex: 4,)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's the resulting application.
|
||||
|
||||
![Flutter Badges plugin][14]
|
||||
|
||||
### Flutter Google Maps plugin
|
||||
|
||||
Adding Google Maps in Flutter apps is a very easy process with the help of the **google_maps_flutter** plugin.
|
||||
|
||||
_Note: The Google Maps Flutter plugin is still in developer preview (so it cannot be released to the app store yet). Make sure to wait for the stable release before using it._
|
||||
|
||||
The main prerequisites for using this plugin are to have a [Google Cloud Platform][15] account and to create a Google Maps API key. If you do not know how to obtain a key, see [Google Maps Integration in Flutter][16].
|
||||
|
||||
Once the Google Maps SDK is enabled with a credential/API key, you can use it in your Flutter app. Fill out the following with the key.
|
||||
|
||||
**For Android:**
|
||||
|
||||
Go to **android>app>src>main>Androidmanifest.xml** and make sure that the manifest looks like the following (replacing YOUR KEY HERE with your API key):
|
||||
|
||||
|
||||
```
|
||||
<manifest ...
|
||||
<application ...
|
||||
<meta-data android:name="com.google.android.geo.API_KEY"
|
||||
android:value="YOUR KEY HERE"/>
|
||||
```
|
||||
|
||||
**For iOS:**
|
||||
|
||||
Edit the **Appdelegate.m** file as follows (replacing YOUR KEY HERE with your API key):
|
||||
|
||||
|
||||
```
|
||||
@implementation AppDelegate
|
||||
|
||||
\- (BOOL)application:(UIApplication *)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
[GMSServices provideAPIKey:@"YOUR KEY HERE"];
|
||||
[GeneratedPluginRegistrant registerWithRegistry:self];
|
||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
@end
|
||||
```
|
||||
|
||||
#### GoogleMaps widget basics
|
||||
|
||||
Now it's time to bring the Maps inside the Flutter application. To begin, add the following dependency in the **pubspec.yaml** file:
|
||||
|
||||
|
||||
```
|
||||
dependencies:
|
||||
google_maps_flutter: ^0.5.21
|
||||
```
|
||||
|
||||
Import the following package to the **main.dart** file:
|
||||
|
||||
|
||||
```
|
||||
`import 'package:google_maps_flutter/google_maps_flutter.dart';`
|
||||
```
|
||||
|
||||
This package provides the following widgets:
|
||||
|
||||
##### GoogleMap
|
||||
|
||||
The GoogleMap widget provides the main control over Google Maps inside a Flutter application. It has several important attributes that help create the maps you require. They are:
|
||||
|
||||
* **mapType:** This attribute defines what type of map (satellite, hybrid, or normal) is shown. Select one by with the value MapType.satellite, MapType.hybrid, or MapType.normal.
|
||||
* **InitialCameraPosition:** The initial camera position is important for rendering the map on the Flutter UI and setting the camera position (from which the camera will move). Set the initial camera position by creating a variable with the **CameraPosition** class as its value.
|
||||
* **OnMapCreated:** This is a callback that fires whenever the camera position changes (e.g., whenever the user moves the map by pinching or swiping it). To move the camera angle programmatically, use GoogleMapController instead.
|
||||
|
||||
|
||||
|
||||
##### GoogleMapController
|
||||
|
||||
This class controls the Google Map by creating an instance of it. There is no explicit way to change the camera position of the Google Map, but you can use the GoogleMapController to control all sorts of activities on the GoogleMap class.
|
||||
|
||||
##### CameraPosition
|
||||
|
||||
The CameraPosition class provides the camera position values that are required to show any position on the GoogleMap.
|
||||
|
||||
|
||||
```
|
||||
CameraPosition initPosition = CameraPosition(
|
||||
target: LatLng(14.5, 25.7), zoom: 7, );
|
||||
```
|
||||
|
||||
The CameraPosition class takes in various attributes, like target, zoom, etc. The **Target** attribute marks the latitude and longitude position on the Google Map. The class takes in a double value like **LatLng(double, double)** to mark it at that position.
|
||||
|
||||
#### Example Flutter GoogleMaps app
|
||||
|
||||
This example app creates an animated camera transition on a Google Map. This is a very useful way to provide [Google Maps in Flutter][16] applications.
|
||||
|
||||
|
||||
```
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
|
||||
void main() => runApp(GoogleMapApp());
|
||||
|
||||
class GoogleMapApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Sample GoogleMap Widget',
|
||||
home: GoogleMapWidget(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GoogleMapWidget extends StatefulWidget {
|
||||
GoogleMapWidget({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_GoogleMapWidgetState createState() => _GoogleMapWidgetState();
|
||||
}
|
||||
|
||||
class _GoogleMapWidgetState extends State {
|
||||
|
||||
Completer _controller = Completer();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
CameraPosition initPosition = CameraPosition(
|
||||
target: LatLng(14.5, 25.7),
|
||||
zoom: 7,
|
||||
);
|
||||
|
||||
void updateGoogleMap()
|
||||
async{
|
||||
GoogleMapController cont = await _controller.future;
|
||||
setState(() {
|
||||
CameraPosition newtPosition = CameraPosition(
|
||||
target: LatLng(14.5, 28.7),
|
||||
zoom: 4,
|
||||
);
|
||||
cont.animateCamera(CameraUpdate.newCameraPosition(newtPosition));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.black45,
|
||||
title: Text("Update Google Map"),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 400.0,
|
||||
child: GoogleMap(
|
||||
mapType: MapType.hybrid,
|
||||
initialCameraPosition: initPosition,
|
||||
onMapCreated: (GoogleMapController controller){
|
||||
_controller.complete(controller);
|
||||
},
|
||||
),
|
||||
),
|
||||
FlatButton(
|
||||
child: Text("Update Map", style: TextStyle(color: Colors.white),),
|
||||
color: Colors.deepOrange,
|
||||
onPressed: (){
|
||||
updateGoogleMap();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's the resulting application.
|
||||
|
||||
![Flutter Google Maps plugin][17]
|
||||
|
||||
![Flutter Google Maps plugin][18]
|
||||
|
||||
### Flutter ImagePicker image gallery plugin
|
||||
|
||||
The ImagePicker plugin integrates an image gallery into a Flutter app.
|
||||
|
||||
To begin using the [**image_picker**][19] plugin, add the following dependency in the **pubspec.yaml** file:
|
||||
|
||||
|
||||
```
|
||||
dependencies:
|
||||
image_picker: ^0.6.1+4
|
||||
```
|
||||
|
||||
This requires you to add an import statement in your main file, e.g., **main.dart** file:
|
||||
|
||||
|
||||
```
|
||||
`import 'package:image_picker/image_picker.dart';`
|
||||
```
|
||||
|
||||
To use the Flutter application in iOS, make the following changes in the **info.plist** file:
|
||||
|
||||
* **NSPhotoLibraryUsageDescription:** This describes why the app needs permission to use the photo library. This is called _Privacy - Photo Library Usage Description_ in the visual editor.
|
||||
* **NSCameraUsageDescription:** This describes why your app needs access to the camera. This is called _Privacy - Camera Usage Description_ in the visual editor.
|
||||
* **NSMicrophoneUsageDescription:** This describes why your app needs access to the microphone if you intend to record videos. This is called _Privacy - Microphone Usage Description_ in the visual editor.
|
||||
|
||||
|
||||
|
||||
#### Image Picker widget basics
|
||||
|
||||
To use the ImagePicker widget, just call the class [**ImagePicker**][20]. There are two options for this class:
|
||||
|
||||
* Choose an image or choose a video
|
||||
* Choose an image or video directly from a gallery or a camera source
|
||||
|
||||
|
||||
|
||||
This is possible through two method callbacks:
|
||||
|
||||
* **ImagePicker.pickImage()** with the source **ImageSource.gallery** or **ImageSource.camera**
|
||||
* **ImagePicker.pickVideo()** with the above sources
|
||||
|
||||
|
||||
|
||||
Both of these calls are async calls, which require **setState()** on the image or video that is selected.
|
||||
|
||||
**ImagePicker.<source call>** returns the file location of the image/video. You must load the image using the **Image.file()** call.
|
||||
|
||||
All of this is explained in the example application.
|
||||
|
||||
#### Example Flutter ImagePicker widget app
|
||||
|
||||
This example app creates an [Image Picker button][20] to select an image from the gallery or directly from the camera.
|
||||
|
||||
|
||||
```
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
void main() => runApp(ImagePickerApp());
|
||||
|
||||
class ImagePickerApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Sample Imagepicker Widget',
|
||||
home: ImagePickerWidget(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ImagePickerWidget extends StatefulWidget {
|
||||
ImagePickerWidget({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ImagePickerWidgetState createState() => _ImagePickerWidgetState();
|
||||
}
|
||||
|
||||
class _ImagePickerWidgetState extends State {
|
||||
|
||||
File _image;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void open_camera()
|
||||
async {
|
||||
var image = await ImagePicker.pickImage(source: ImageSource.camera);
|
||||
setState(() {
|
||||
_image = image;
|
||||
});
|
||||
|
||||
}
|
||||
void open_gallery()
|
||||
async {
|
||||
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
|
||||
setState(() {
|
||||
_image = image;
|
||||
});
|
||||
}
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text("Sample Imagepicker Widget"),
|
||||
backgroundColor: Colors.black45,),
|
||||
body: Center(
|
||||
child: Container(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
color: Colors.black12,
|
||||
height: 300.0,
|
||||
width: 900.0,
|
||||
child: _image == null ? Text("Still waiting!") : Image.file(_image),),
|
||||
FlatButton(
|
||||
color: Colors.deepOrangeAccent,
|
||||
child: Text("Open Camera", style: TextStyle(color: Colors.white),),
|
||||
onPressed: (){
|
||||
open_camera();
|
||||
},),
|
||||
FlatButton(
|
||||
color: Colors.limeAccent,
|
||||
|
||||
child:Text("Open Gallery", style: TextStyle(color: Colors.black),),
|
||||
onPressed: (){
|
||||
open_gallery();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's the resulting application.
|
||||
|
||||
![Flutter ImagePicker widget][21]
|
||||
|
||||
![Flutter ImagePicker widget][22]
|
||||
|
||||
![Flutter ImagePicker widget][23]
|
||||
|
||||
### Summary
|
||||
|
||||
These five plugins are very important for creating a neater UI experience in Flutter apps. They will also help you ramp up faster with Flutter app development.
|
||||
|
||||
* * *
|
||||
|
||||
_Some of the information in this article was previously published at [Android Monks][24]._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/11/open-source-plugins-flutter-apps
|
||||
|
||||
作者:[Baradwaj Varadharajan][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/baradwaj
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/bug-insect-butterfly-diversity-inclusion-2.png?itok=TcC9eews
|
||||
[2]: https://opensource.com/article/18/6/flutter
|
||||
[3]: https://flutter.dev/
|
||||
[4]: https://en.wikipedia.org/wiki/Google_Fuchsia
|
||||
[5]: https://androidmonks.com/scaffold-flutter/
|
||||
[6]: https://opensource.com/sites/default/files/uploads/flutter1_videoplayer.png (Flutter Video Plugin)
|
||||
[7]: https://opensource.com/sites/default/files/uploads/flutter2_videoplayer.png (Flutter Video Plugin)
|
||||
[8]: https://androidmonks.com/shimmer-effect-flutter/
|
||||
[9]: https://opensource.com/sites/default/files/uploads/flutter3_shimmergif.gif (Flutter Shimmer Effect plugin)
|
||||
[10]: https://androidmonks.com/listview-flutter/
|
||||
[11]: https://opensource.com/sites/default/files/uploads/flutter4_shimmer.png (Flutter Shimmer Effect plugin)
|
||||
[12]: https://opensource.com/sites/default/files/uploads/flutter5_shimmer.png (Flutter Shimmer Effect plugin)
|
||||
[13]: https://androidmonks.com/flutter-badges/
|
||||
[14]: https://opensource.com/sites/default/files/uploads/flutter6_samplebadges.png (Flutter Badges plugin)
|
||||
[15]: https://cloud.google.com/maps-platform/
|
||||
[16]: https://androidmonks.com/google-maps-flutter/
|
||||
[17]: https://opensource.com/sites/default/files/uploads/flutter7_googlemap.png (Flutter Google Maps plugin)
|
||||
[18]: https://opensource.com/sites/default/files/uploads/flutter8_googlemap.png (Flutter Google Maps plugin)
|
||||
[19]: https://pub.dev/packages/image_picker
|
||||
[20]: https://androidmonks.com/imagepicker-flutter/
|
||||
[21]: https://opensource.com/sites/default/files/uploads/flutter9_imagepicker.png (Flutter ImagePicker widget)
|
||||
[22]: https://opensource.com/sites/default/files/uploads/flutter10_imagepicker.png (Flutter ImagePicker widget)
|
||||
[23]: https://opensource.com/sites/default/files/uploads/flutter11_imagepicker.png (Flutter ImagePicker widget)
|
||||
[24]: https://androidmonks.com/flutter-open-source-plugins/
|
||||
@@ -1,231 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Monitoring Linux and Windows hosts with Glances)
|
||||
[#]: via: (https://opensource.com/article/19/11/monitoring-linux-glances)
|
||||
[#]: author: (David Both https://opensource.com/users/dboth)
|
||||
|
||||
Monitoring Linux and Windows hosts with Glances
|
||||
======
|
||||
The Glances tool offers more information about system performance than
|
||||
other sysadmin monitoring tools.
|
||||
![Code going into a computer.][1]
|
||||
|
||||
Sysadmins have many tools to view and manage running processes. For me, these primarily used to be **top**, **atop**, and **htop**. A few years ago, I found [Glances][2], a tool that displays information that none of my other favorites do. All of these tools monitor CPU and memory usage, and most of them list information about running processes (at the very least). However, Glances also monitors filesystem I/O, network I/O, and sensor readouts that can display CPU and other hardware temperatures as well as fan speeds and disk usage by hardware device and logical volume.
|
||||
|
||||
### Glances
|
||||
|
||||
I mentioned Glances in my article [_4 open source tools for Linux system monitoring_][3], but I will delve into it more deeply in this article. If you read my previous article, some of this information may be familiar, but you should also find some new things here.
|
||||
|
||||
Glances is cross-platform because it is written in Python. It can be installed on Windows and other hosts with current versions of Python installed. Most Linux distributions (Fedora in my case) have Glances in their repositories. If not, or if you are using a different operating system (such as Windows), or you just want to get it right from the source, you can find instructions for downloading and installing it in [Glances' GitHub repo][4].
|
||||
|
||||
I suggest running Glances on a test machine while you try the commands in this article. If you don't have a physical host available for testing, you can explore Glances on a virtual machine (VM), but you won't see the hardware sensors section; after all, a VM has no real hardware.
|
||||
|
||||
To start Glances on a Linux host, open a terminal session and enter the command **glances**.
|
||||
|
||||
Glances has three main sections—Summary, Process, and Alerts—as well as a sidebar. I'll explore them and other details for using Glances now.
|
||||
|
||||
### Summary section
|
||||
|
||||
In its top few lines, Glances' Summary section contains much of the same information as you'll find in other monitors' summary sections. If you have enough horizontal space in your terminal, Glances can show CPU usage with both a bar graph and a numeric indicator; otherwise it will show only the number.
|
||||
|
||||
I like Glances' Summary section better than the ones in other monitors (like **top**); I think it provides the right information in an easily understandable format.
|
||||
|
||||
![Glances display][5]
|
||||
|
||||
The Glances display on a busy Linux host. In the interest of clarity, not all possible displays are shown in the left sidebar.
|
||||
|
||||
The Summary section above provides an overview of the system's status. The first line shows the hostname, the Linux distribution, the kernel version, and the system uptime.
|
||||
|
||||
The next four lines display CPU, memory usage, swap, and load statistics. The left column displays the percentages of CPU, memory, and swap space that are in use. It also shows the combined statistics for all CPUs present in the system.
|
||||
|
||||
Press the **1** key to toggle between the consolidated CPU usage display and the display of the individual CPUs. The following image shows the Glances display with individual CPU statistics.
|
||||
|
||||
![Glances display][6]
|
||||
|
||||
Glances showing the individual CPU statistics.
|
||||
|
||||
This view includes some additional CPU statistics. In either display mode, the descriptions of the CPU usage fields can help you interpret the data displayed in the CPU section. Notice that CPUs are numbered starting at 0 (Zero).
|
||||
|
||||
**CPU** | This is the current CPU usage as a percentage of the total available.
|
||||
---|---
|
||||
**user** | These are the applications and other programs running in user space, i.e., not in the kernel.
|
||||
**system** | These are kernel-level functions. It does not include CPU time taken by the kernel itself, just the kernel system calls.
|
||||
**idle** | This is the idle time, i.e., time not used by any running process.
|
||||
**nice** | This is the time used by processes that are running at a positive, nice level.
|
||||
**irq** | These are the interrupt requests that take CPU time.
|
||||
**iowait** | These are CPU cycles that are spent waiting for I/O to occur—this is wasted CPU time.
|
||||
**steal** | The percentage of CPU cycles that a virtual CPU waits for a real CPU while the hypervisor is servicing another virtual processor.
|
||||
**ctx-sw** | These are the number of context switches per second; it represents the number of times per second that the CPU switches from running one process to another.
|
||||
**inter** | This is the number of hardware interrupts per second. A hardware interrupt occurs when a hardware device, such as a hard drive, tells a CPU that it has completed a data transfer or that a network interface card is ready to accept more data.
|
||||
**sw_int** | Software interrupts tell the CPU that some requested task has completed or that the software is ready for something. These tend to be more common in kernel-level software.
|
||||
|
||||
#### About nice numbers
|
||||
|
||||
Nice numbers are the mechanism used by administrators to affect the priority of a process. It is not possible to change the priority of a process directly, but changing the nice number can modify the results of the kernel scheduler’s priority setting algorithm. Nice numbers run from -20 to +19 where higher numbers are nicer. The default nice number is 0 and the default priority is 20. Setting the nice number higher than zero increases the priority number somewhat, thus making the process nicer and therefore less greedy of CPU cycles. Setting the nice number to a more negative number results in a lower priority number making the process less nice. Nice numbers can be changed using the renice command or from within top, atop, and htop.
|
||||
|
||||
#### Memory
|
||||
|
||||
The Memory portion of the Summary section contains statistics about memory usage.
|
||||
|
||||
**MEM** | This shows the memory usage as a percent of the total amount available.
|
||||
---|---
|
||||
**total** | This is the total amount of RAM memory installed in the host, less any amount assigned to the display adapter.
|
||||
**used** | This is the total amount of memory in use by the system and application programs but not including cache or buffers.
|
||||
**free** | This is the amount of free memory.
|
||||
**active** | This is the amount of actively used memory—inactive memory is subject to swapping to disk should the need arise.
|
||||
**inactive** | This is memory that is in use but that has not been accessed for some time.
|
||||
**buffers** | This is memory that is used for buffer space; it is usually used by communications and I/O such as networking. The data is received and stored until the software can retrieve it for use or it can be sent to a storage device or transmitted out to the network.
|
||||
**cached** | This is the memory used to store data for disk transfer until it can be used by a program or stored to disk.
|
||||
|
||||
The Swap section is self-explanatory if you understand a bit about swap space and how it works. This shows how much total swap space is available, how much is used, and how much is left.
|
||||
|
||||
The Load part of the Summary section displays the one-, five-, and 15-minute load averages.
|
||||
|
||||
You can use the numeric keys **1**, **3**, **4**, and **5** to alter your view of the data in this section. The **2** key toggles the left sidebar on and off.
|
||||
|
||||
#### More about load averages
|
||||
|
||||
Load averages are commonly misunderstood, even though they are a key criterion for measuring CPU usage. But what does it really mean when I say that the one- (or five- or 10-) minute load average is 4.04, for example? Load average can be considered a measure of demand for the CPU; it is a number that represents the average number of instructions waiting for CPU time, so it is a true measure of CPU performance.
|
||||
|
||||
For example, a fully utilized single-processor system CPU would have a load average of 1. This means that the CPU is keeping up exactly with demand; in other words, it has perfect utilization. A load average of less than 1 means the CPU is underutilized, and a load average greater than 1 means the CPU is overutilized and that there is pent-up, unsatisfied demand. For example, a load average of 1.5 in a single-CPU system indicates that one-third of the CPU instructions must wait to be executed until the preceding one has completed.
|
||||
|
||||
This is also true for multiple processors. If a four-CPU system has a load average of 4, then it has perfect utilization. If it has a load average of 3.24, for example, then three of its processors are fully utilized, and one is utilized at about 24%. In the example above, a four-CPU system has a one-minute load average of 4.04, meaning there is no remaining capacity among the four CPUs, and a few instructions are forced to wait. A perfectly utilized four-CPU system would show a load average of 4.00, meaning that the system is fully loaded but not overloaded.
|
||||
|
||||
The optimum load average condition is for the load average to equal the total number of CPUs in a system. That would mean that every CPU is fully utilized, and no instruction must be forced to wait. But reality is messy, and optimum conditions are seldom met. If a host were running at 100% utilization, this would not allow for spikes in CPU load requirements.
|
||||
|
||||
The longer-term load averages indicate overall utilization trends.
|
||||
|
||||
_Linux Journal_ published an excellent article [about load averages][7], the theory, the math behind them, and how to interpret them, in its December 1, 2006, issue. Unfortunately, _Linux Journal_ has ceased publication, and its archives are no longer available directly, so the link is to a third-party archive.
|
||||
|
||||
#### Finding CPU hogs
|
||||
|
||||
One of the reasons for using a tool like Glances is to find processes that are taking up too much CPU time. Open a new terminal session (different from the one running Glances), and enter and start the following CPU-hogging Bash program.
|
||||
|
||||
|
||||
```
|
||||
`X=0;while [ 1 ];do echo $X;X=$((X+1));done`
|
||||
```
|
||||
|
||||
This program is a CPU hog and will use up every available CPU cycle. Allow it to run while you finish this article and experiment with Glances. It will provide you with an idea of what a program that hogs CPU cycles looks like. Be sure to observe the effects on the load averages over time, as well as the cumulative time in the **TIME+** column for this process.
|
||||
|
||||
### Process section
|
||||
|
||||
The Process section displays standard information about each process that is running. Depending upon the viewing mode and the size of the terminal screen, different columns of information will be displayed for the running processes. The default mode with a wide-enough terminal displays the columns listed below. The columns that are displayed change automatically if the terminal screen is resized. The following columns are typically displayed for each process from left to right.
|
||||
|
||||
**CPU%** | This is the amount of CPU time as a percentage of a single core. For example, 98% represents 98% of the available CPU cycles for a single core. Multiple processes can show up to 100% CPU usage.
|
||||
---|---
|
||||
**MEM%** | This is the amount of RAM memory used by the process as a percentage of the total virtual memory in the host.
|
||||
**VIRT** | This is the amount of virtual memory used by the process in human-readable format, such as 12M for 12 megabytes.
|
||||
**RES** | This refers to the amount of physical (resident) memory used by the process. Again, this is in human-readable format, with an indicator of **K**, **M**, or **G**, to specify kilobytes, megabytes, or gigabytes.
|
||||
**PID** | Every process has an identification number, called the PID. This number can be used in commands, such as **renice** and **kill**, to manage the process. Remember that the **kill** utility can send signals to another process besides the “kill” signal.
|
||||
**USER** | This is the name of the user that owns the process.
|
||||
**TIME+** | This indicates the cumulative amount of CPU time accrued by the process since it started.
|
||||
**THR** | This is the total number of threads currently running for this process.
|
||||
**NI** | This is the nice number of the process.
|
||||
**S** | This is the current status; it can be (**R**)unning, (**S**)leeping, (**I**)dle, **T** or **t** when the process is stopped during a debugging trace, or (**Z**)ombie. A zombie is a process that has been killed but has not completely died, so it continues to consume some system resources, such as RAM.
|
||||
**R/s and W/s** | These are the disk reads and writes per second.
|
||||
**Command** | This is the command used to start the process.
|
||||
|
||||
Glances usually determines the default sort column automatically. Processes can be sorted automatically (**a**), or by CPU (**c**), memory (**m**), name (**p**), user (**u**), I/O rate (**i**), or time (**t**). Processes are automatically sorted by the most-used resource. In the images above, the **TIME+** column is highlighted.
|
||||
|
||||
### Alerts section
|
||||
|
||||
Glances also shows warnings and critical alerts, including the time and duration of the event, at the bottom of the screen. This can be helpful when you're attempting to diagnose problems and cannot stare at the screen for hours at a time. These alert logs can be toggled on or off with the **l** (lower-case L) key, warnings can be cleared with the **w** key, while alerts and warnings can all be cleared with **x**.
|
||||
|
||||
### Sidebar
|
||||
|
||||
Glances has a very nice sidebar on the left that displays information that is not available in **top** or **htop**. While **atop** displays some of this data, Glances is the only monitor that displays data about sensors. After all, sometimes it is nice to see the temperatures inside your computer.
|
||||
|
||||
The individual modules, disk, filesystem, network, and sensors can be toggled on and off using the **d**, **f**, **n**, and **s** keys, respectively. The entire sidebar can be toggled using **2**. Docker stats can be displayed in the sidebar with **D**.
|
||||
|
||||
Note that hardware sensors are not displayed when Glances is running on a virtual machine.
|
||||
|
||||
### Getting help
|
||||
|
||||
You can get help by pressing the **h** key; dismiss the help page by pressing **h** again. The Help page is rather terse, but it does show the available interactive options and how to turn them on and off. The [man page][8] has terse explanations of the options that can be used when launching Glances.
|
||||
|
||||
You can press **q** or **Esc** to exit Glances.
|
||||
|
||||
### Configuration
|
||||
|
||||
Glances does not require a configuration file to work properly. If you choose to have one, the system-wide instance of the configuration file will be located in **/etc/glances/glances.conf**. Individual users can have a local instance at **~/.config/glances/glances.conf**, which will override the global configuration. The primary purpose of these configuration files is to set thresholds for warnings and critical alerts. You can also specify whether certain modules are displayed by default or not.
|
||||
|
||||
The file **/usr/local/share/doc/glances/README.rst** contains additional useful information, including optional Python modules you can install to support some optional Glances features.
|
||||
|
||||
### Command-line options
|
||||
|
||||
Glances provides command-line options that allow it to start up in specific viewing modes. For example, the command **glances -2** starts the program with the left sidebar disabled.
|
||||
|
||||
### Remote and more
|
||||
|
||||
By starting it in server mode, you can use Glances to monitor remote hosts:
|
||||
|
||||
|
||||
```
|
||||
`[root@testvm1 ~]# glances -s`
|
||||
```
|
||||
|
||||
You can then connect to the server from the client with:
|
||||
|
||||
|
||||
```
|
||||
`[root@testvm2 ~]# glances -c @testvm1`
|
||||
```
|
||||
|
||||
Glances can show a list of Glances servers along with a summary of their activity. It also has a web interface so you can monitor remote Glances servers from a browser. Recent versions of Glances can also display Docker statistics.
|
||||
|
||||
There are also pluggable modules for Glances that provide measurement data not available in the base program.
|
||||
|
||||
### Limitations
|
||||
|
||||
Although Glances can monitor many aspects of a host, it cannot manage processes. It cannot change the nice number of a process nor kill one, as **top** and **htop** can. Glances is not an interactive tool. It is used strictly for monitoring. External tools like **kill** and **renice** can be used to manage processes.
|
||||
|
||||
Glances can only show the processes that are taking the majority of the resource specified, such as CPU time, in the available space. If there is room to list just 10 processes, that is all you will be able to see. Glances does not provide scrolling or reverse-sort options that would enable you to see any other than the top X processes.
|
||||
|
||||
### The impact of measurement
|
||||
|
||||
The [observer effect][9] is a physics theory that states, "simply observing a situation or phenomenon necessarily changes that phenomenon." This is also true when measuring Linux system performance.
|
||||
|
||||
Merely using a monitoring tool alters the system's use of resources, including memory and CPU time. The **top** utility and most other monitors use perhaps 2% tor 3% of a system's CPU time. The Glances utility has much more impact than the others; it usually uses between 10% and 20% of CPU time, and I have seen it use as much as 40% of one CPU in a very large and active system with 32 CPUs. That is a lot, so consider its impact when you think about using Glances as your monitor.
|
||||
|
||||
My personal opinion is that this is a small price to pay when you need the capabilities of Glances.
|
||||
|
||||
### Summary
|
||||
|
||||
Despite its lack of interactive capabilities, such as the ability to **renice** or **kill** processes, and its high CPU load, I find Glances to be a very useful tool. The complete [Glances documentation][10] is available on the internet, and the Glances man page has startup options and interactive command information.
|
||||
|
||||
* * *
|
||||
|
||||
_Parts of this article are based on David Both's new book, _[Using and Administering Linux: Volume 2 – Zero to SysAdmin: Advanced Topics][11].__
|
||||
|
||||
David Both shares his favorite system monitoring tools for understanding what is going on in any...
|
||||
|
||||
David Both explains the importance of keeping hardware cool and shares some Linux tools that can...
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/11/monitoring-linux-glances
|
||||
|
||||
作者:[David Both][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/dboth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/code_computer_development_programming.png?itok=4OM29-82 (Code going into a computer.)
|
||||
[2]: https://nicolargo.github.io/glances/
|
||||
[3]: https://opensource.com/life/16/2/open-source-tools-system-monitoring
|
||||
[4]: https://github.com/nicolargo/glances/blob/master/README.rst#installation
|
||||
[5]: https://opensource.com/sites/default/files/uploads/glances-figure-1.png (Glances display)
|
||||
[6]: https://opensource.com/sites/default/files/uploads/glances-figure-2.png (Glances display)
|
||||
[7]: https://archive.org/details/Linux-Journal-2006-12/page/n81
|
||||
[8]: https://linux.die.net/man/1/glances
|
||||
[9]: https://en.m.wikipedia.org/wiki/Observer_effect_(physics)
|
||||
[10]: https://glances.readthedocs.io/en/stable/
|
||||
[11]: https://www.apress.com/us/book/9781484250488
|
||||
@@ -1,106 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Tracking Translations with Transtats)
|
||||
[#]: via: (https://fedoramagazine.org/tracking-translations-with-transtats/)
|
||||
[#]: author: (Sundeep Anand https://fedoramagazine.org/author/suanand/)
|
||||
|
||||
Tracking Translations with Transtats
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
Translation is an important step in software localization which helps make software more popular globally, and impacts international user experience. In recent years, localization processes have been evolving worldwide to become more continuous, faster, efficient with automation. In Fedora, the development of the [Zanata][2] platform and its plugins, then [Transtats][3], and now the migration to the [Weblate][4] platform are part of this common ongoing goal. The localization of a desktop OS like Fedora is highly complex because it depends on many factors of the individual upstream projects which are packaged in Fedora. For example, different translation timelines, resources, and tooling.
|
||||
|
||||
### What is Transtats?
|
||||
|
||||
Transtats is a web application which tries to tie up upstream repositories, translation platforms, build system, and product release schedule together to solve problems of mismatch, out-of-sync conditions and to assist the timely packaging of quality translations. Actually, it collects translation data, analyzes them, and creates meaningful representations.
|
||||
|
||||
Fedora Transtats is hosted at <https://transtats.fedoraproject.org/>
|
||||
|
||||
![][5]
|
||||
|
||||
### How to see the translation status of my package?
|
||||
|
||||
Just select **Packages** tab from left hand side navigation bar. This takes us to the packages list view. Then, search for the package and click on its name.
|
||||
|
||||
![][6]
|
||||
|
||||
For example _anaconda_. On package details page, locate following:
|
||||
|
||||
![][7]
|
||||
|
||||
Here, we have translation statistics from translation platform: [Zanata][8] and [Koji][9] build system. Syncs with the platform and build system are scheduled, which update differences periodically. Languages in red color indicate that there are translated strings remaining in the Translation Platform to be pulled and packaged, whereas, blue denote translated messages could not make 100% in the built package.
|
||||
|
||||
### String breakage (or changes?)
|
||||
|
||||
In translation of software packages, one of the challenges is to prevent string breakage. Package maintainers should strive to abide by the scheduled Fedora release String Freeze. However, in some circumstances it could be necessary to break the string freeze and to inform the translation team on the mailing list. As well as, to update latest translation template (POT) file in the translation platform. Just in case these actions seem missing – translators may get new strings to translate very late or the application may have some strings untranslated. In the worst case, an outdated translation string mismatch may result in a crash. Sync and automation pipelines are there to prevent this, nevertheless it depends on the push or pull methods followed by package developers or maintainers.
|
||||
|
||||
To deal with the same context, we can use a job template in Transtats to detect this string change – particularly useful after string freeze in Fedora release schedule. This would be really helpful for the folks who look for packaging translations without string breakage, keeping translation template (POT) file in sync with translation platform, and testing localized form of the application for translation completeness to back trace.
|
||||
|
||||
### How to detect string changes?
|
||||
|
||||
One of the options in **Jobs** tab is ‘YML based Jobs’. Where we can see available job templates.
|
||||
|
||||
![][10]
|
||||
|
||||
The jobs framework executes all the tasks mentioned in the YAML, create appropriate logs and store results. Track String Change job basically:
|
||||
|
||||
1. Clones the source repository of respective package.
|
||||
2. Tries to generate translation template (POT) file.
|
||||
3. Downloads POT file from respective translation platform.
|
||||
4. And, finds differences between both the POT files.
|
||||
|
||||
|
||||
|
||||
_Actually, Transtats maintains mapping of upstream repository, Translation Platform project and respective build tag for every package._
|
||||
|
||||
![][11]
|
||||
|
||||
Let’s take a closer look into this YAML. We can provide value for %PACKAGE_NAME% and %RELEASE_SLUG% in the next step – **Set Values**! For example: _anaconda_ and _fedora-32_. Furthermore, a couple of things seek attention are:
|
||||
|
||||
* In case the upstream software repository maintains separate git branch for fedora release, please edit ‘branch: master’ to ‘branch: <fedora-release-branch>’
|
||||
* In ‘generate’ block, mention the command to generate POT file. Default one should work for ‘intltool-update’ only, however, many packages do have their own.
|
||||
* A few packages may have gettext domain name different than that of package name. If this is the case, mention the gettext domain too.
|
||||
|
||||
|
||||
|
||||
As soon as the job is triggered, logs should be populated. If this is not a _scratch_ run, a unique URL shall also be created at the end.
|
||||
|
||||
![][12]
|
||||
|
||||
Left hand side is the input YAML and right hand side is respective log for each task. Here we can find the differences and figure out string mismatch.
|
||||
|
||||
![][13]
|
||||
|
||||
In [Transtats][14], we can create solutions to different problems in the form of job templates. And, scheduling of these jobs could be a step towards automation.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/tracking-translations-with-transtats/
|
||||
|
||||
作者:[Sundeep Anand][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/suanand/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2019/12/transtats-816x345.png
|
||||
[2]: http://zanata.org/
|
||||
[3]: http://transtats.org/
|
||||
[4]: https://weblate.org/en/
|
||||
[5]: https://fedoramagazine.org/wp-content/uploads/2019/12/transtats-landing-1-1024x450.png
|
||||
[6]: https://fedoramagazine.org/wp-content/uploads/2019/12/anaconda-pkg.png
|
||||
[7]: https://fedoramagazine.org/wp-content/uploads/2019/12/anaconda-details.png
|
||||
[8]: https://fedora.zanata.org/project/view/anaconda
|
||||
[9]: https://koji.fedoraproject.org/koji
|
||||
[10]: https://fedoramagazine.org/wp-content/uploads/2019/12/job-templates-1024x361.png
|
||||
[11]: https://fedoramagazine.org/wp-content/uploads/2019/12/strchgjob.png
|
||||
[12]: https://fedoramagazine.org/wp-content/uploads/2019/12/string-change-job-log-1024x478.png
|
||||
[13]: https://fedoramagazine.org/wp-content/uploads/2019/12/string-change-1024x614.png
|
||||
[14]: https://github.com/transtats/transtats
|
||||
@@ -1,126 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Create fancy text for your social media posts with this Gawk script)
|
||||
[#]: via: (https://opensource.com/article/20/1/gawk-scripting-social-media)
|
||||
[#]: author: (Jim Hall https://opensource.com/users/jim-hall)
|
||||
|
||||
Create fancy text for your social media posts with this Gawk script
|
||||
======
|
||||
Add a little style to your status updates by posting text in script,
|
||||
fraktur, or double-strike characters.
|
||||
![Chat via email][1]
|
||||
|
||||
Like almost everyone on the planet, I have a few social media accounts. I mostly stick to Facebook to stay up to date with friends and family and Twitter to follow a few other people.
|
||||
|
||||
Have you ever wanted to make a post that includes italics or some other fancy formatting? You can easily change the text to italics or bold when you're writing an email, but most social media platforms don't provide many formatting options.
|
||||
|
||||
And sometimes, I just want to put a little _emphasis_ into what I'm writing. If I've had a really good day and I want to share that with my friends, I might want to put that text in italics. For other posts, I might want to use different formatting that will help my text stand out. Sure, you can use emoji, but sometimes a little text formatting can add that extra pizzazz to your posts.
|
||||
|
||||
I found a way to do just that. With a short [Gawk][2] script I wrote, I can create fancy formatting that I can copy and paste into my social media posts.
|
||||
|
||||
### Special HTML
|
||||
|
||||
HTML includes a bunch of special characters for mathematics, symbols, and other languages that most people are not aware of. Within the Mathematical Markup Language ([MathML][3]) math character support, HTML includes alternate versions of the alphabet for script, fraktur, and double-strike (shown respectively in this image) characters.
|
||||
|
||||
![script, fraktur, and double-strike text][4]
|
||||
|
||||
You can use these alternate versions of the alphabet to create fancy text.
|
||||
|
||||
The script alphabet variation is written as the letter followed by **scr**. Characters can be uppercase or lowercase. For example, to print the script letter **a** in an HTML page, type **&ascr;**, and to print the script letter **Z** in HTML, type **&Zscr;**.
|
||||
|
||||
The fraktur and double-strike variations are referenced in similar ways. The fraktur mathematical lower-case **a** is **&afr;**, and the capital **Y** is **&Yfr;**. The mathematical double-strike **a** is referenced as **&aopf;**, and the double-strike **X** is **&Xopf;**.
|
||||
|
||||
### Gawk functions
|
||||
|
||||
Once you know how to reference the alternate versions of each letter, it's easy to define a few Gawk functions to print those HTML entities. Since these alternate characters exist only for letters and not punctuation or numbers, start with a simple wrapper function to determine if a character is an uppercase or lowercase letter.
|
||||
|
||||
|
||||
```
|
||||
#!/usr/bin/gawk -f
|
||||
# Echo the input as different "fonts." Redirect this into an html
|
||||
# page and copy/paste fancy text into twitter or facebook.
|
||||
|
||||
BEGIN { alpha="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
|
||||
|
||||
function is_alpha(c) {
|
||||
return(index(alpha, c));
|
||||
}
|
||||
```
|
||||
|
||||
The **BEGIN** statement defines an alphabet string called **alpha** that contains all letters a–z and A–Z. The **is_alpha(c)** function uses the built-in **index()** function to return the location of the character **c** in the string **alpha**. If the character **c** is not a letter, **index()** returns zero, which the script uses as a False value.
|
||||
|
||||
Because the **is_alpha(c)** function just "wraps" a call to the **index()** function without doing anything else; this is called a _wrapper_ function. Think of it as shorthand that makes the Gawk script easier to read.
|
||||
|
||||
With that, it's easy to define a few functions that convert single letters into each of the alternate versions of the alphabet. In general, each function calls **is_alpha(c)** to determine if a character is a letter a–z or A–Z. If it is (i.e., if the returned value is non-zero), then the function prints the HTML entity for that letter as script, fraktur, and double-strike. Otherwise, the function prints the letter.
|
||||
|
||||
|
||||
```
|
||||
function print_script(c) {
|
||||
if ( is_alpha(c) ) { printf("&%cscr;", c); } else { printf("%c", c); }
|
||||
}
|
||||
function print_fraktur(c) {
|
||||
if ( is_alpha(c) ) { printf("&%cfr;", c); } else { printf("%c", c); }
|
||||
}
|
||||
function print_double(c) {
|
||||
if ( is_alpha(c) ) { printf("&%copf;", c); } else { printf("%c", c); }
|
||||
}
|
||||
```
|
||||
|
||||
The **print_script(c)** function prints a single letter in script. Similarly, the **print_fraktur(c)** function prints a letter in fraktur, and the **print_double(c)** function prints a single letter in double-strike.
|
||||
|
||||
All that's left is a Gawk loop to convert plain text into each of the alternate alphabet characters. This script loops over each line three times and prints the text in script, fraktur, or double-strike. Each line is wrapped in **<p>** and **</p>** HTML tags.
|
||||
|
||||
|
||||
```
|
||||
{ text=$0;
|
||||
len=length(text);
|
||||
|
||||
print "<p>";
|
||||
for (i=1; i<=len; i++) {
|
||||
print_script( substr(text, i, 1) );
|
||||
}
|
||||
print "</p><p>";
|
||||
for (i=1; i<=len; i++) {
|
||||
print_fraktur( substr(text, i, 1) );
|
||||
}
|
||||
print "</p><p>";
|
||||
for (i=1; i<=len; i++) {
|
||||
print_double( substr(text, i, 1) );
|
||||
}
|
||||
print "</p>";
|
||||
}
|
||||
```
|
||||
|
||||
### Putting it all together
|
||||
|
||||
I saved the above lines in a script file called **htmlecho** and put it in my **~/bin** directory.
|
||||
|
||||
|
||||
```
|
||||
$ htmlecho > /tmp/hello.html
|
||||
Hello world
|
||||
^Z
|
||||
```
|
||||
|
||||
Whenever I want to add fancy text to my Facebook and Twitter posts, I just run the script and save the output to a temporary HTML page. I open the temporary page in a web browser and copy and paste the fancy text I like best into my social media posts.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/gawk-scripting-social-media
|
||||
|
||||
作者:[Jim Hall][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jim-hall
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/email_chat_communication_message.png?itok=LKjiLnQu (Chat via email)
|
||||
[2]: https://www.gnu.org/software/gawk/
|
||||
[3]: https://en.wikipedia.org/wiki/MathML
|
||||
[4]: https://opensource.com/sites/default/files/uploads/hello_world.png (script, fraktur, and double-strike text)
|
||||
@@ -1,107 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (App Highlight: Catfish Desktop File Searching Tool)
|
||||
[#]: via: (https://itsfoss.com/catfish/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
App Highlight: Catfish Desktop File Searching Tool
|
||||
======
|
||||
|
||||
_****Brief: Catfish is a nifty file searching GUI tool for Linux desktop. The interface is lightweight and simple and the tool allows to refine your search with** criteria like time, file type etc.**_
|
||||
|
||||
The Linux purists use commands like locate, find and grep to search for files in the terminal.
|
||||
|
||||
But as a desktop Linux user, you don’t need to leave the comfort of the graphical user interface (GUI) and deep dive into the command line interface (CLI) just for searching files on your desktop.
|
||||
|
||||
Most Linux distributions provide a basic desktop search feature either via the file manager or through the desktop environment itself.
|
||||
|
||||
On GNOME desktop, you can search for files in the Activities area (use the Windows key to bring it up). Files (previously known as Nautilus) also has a built-in search button.
|
||||
|
||||
![Nautilus file manager already has advanced search feature][1]
|
||||
|
||||
You can extend this search and add options like time and type of the file. One thing it doesn’t do is to search inside the files. For example, you cannot use it to get all the files that contains “university”.
|
||||
|
||||
This is where a desktop file search tool like Catfish could help you.
|
||||
|
||||
### Catfish: A nifty GUI tool for searching files on Linux
|
||||
|
||||
[Catfish][2] is a GUI tool that enables you to search your desktop for any kind of files. It uses locate and find commands underneath. The autocompletion feature uses [Zeitgeist][3] daemon and [locate command][4]. It’s a lightweight tool and uses GTK+.
|
||||
|
||||
Catfish is developed by [Christian Dywan][5], the same person who develops the [lightweight Midori web browser][6].
|
||||
|
||||
![Catfish interface on MX Linux][7]
|
||||
|
||||
Some of the main features of Catfish are:
|
||||
|
||||
* Search for files anywhere on your system, **including the mounted partitions**
|
||||
* **Search inside the files** for its contents (can be enabled from preferences)
|
||||
* Search hidden files as well
|
||||
* Refine your search based on modification time
|
||||
* Refine your search based on file type (images, videos, documents etc)
|
||||
* Refine your search based on location (Documents, Downloads, Pictures or other folders)
|
||||
* Exclude certain directories and paths from your search
|
||||
* Lightweight and simple interface
|
||||
* **Support for Wayland** display server (from version 1.4.12)
|
||||
|
||||
|
||||
|
||||
Catfish is now a Xfce project and it is providing the search feature to Xfce’s Thunar file manager.
|
||||
|
||||
### Installing Catfish on Ubuntu and other Linux distributions
|
||||
|
||||
Let’s see how to install Catfish on your Linux distributions.
|
||||
|
||||
**Ubuntu-based distributions**
|
||||
|
||||
Catfish is available in the universe repository for Ubuntu based distributions such as Xubuntu, Linux Mint, Linux Lite etc.
|
||||
|
||||
You can install it from the software center by searching for Catfish
|
||||
|
||||
![Catfish in Ubuntu Software Center][8]
|
||||
|
||||
or, use the terminal to install it:
|
||||
|
||||
```
|
||||
sudo apt install catfish
|
||||
```
|
||||
|
||||
The version provided by Ubuntu may not be the latest. The [official PPA][9] has been abandoned so this means that to get the latest Catfish version, you’ll have to [install it from the source code][10].
|
||||
|
||||
**On other distributions**
|
||||
|
||||
Catfish is also available in most major Linux distributions. It is certainly available on Fedora and if you check your distribution’s package manager or software center, you should find it there and install it like any other program.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
In this week’s Linux application highlight, you learned about this handy little utility. However, Catfish is not the only tool of its kind. You may check some other search tools like [ANGRYSearch][11] or [SearchMonkey][12].
|
||||
|
||||
Have you ever used a GUI tool for searching files or do you rely on the good old command line? And what do you think of Catfish? Do you look forward to use it?
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/catfish/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/abhishek/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/01/search-files-gnome.jpg?ssl=1
|
||||
[2]: https://launchpad.net/catfish-search/
|
||||
[3]: https://wiki.archlinux.org/index.php/Zeitgeist
|
||||
[4]: https://linuxhandbook.com/locate-command/
|
||||
[5]: http://www.twotoasts.de/index.php/about/
|
||||
[6]: https://itsfoss.com/midori-browser/
|
||||
[7]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/01/catfish_screenshot.png?ssl=1
|
||||
[8]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/01/catfish_ubuntu_software_center.jpg?ssl=1
|
||||
[9]: https://itsfoss.com/ppa-guide/
|
||||
[10]: https://itsfoss.com/install-software-from-source-code/
|
||||
[11]: https://itsfoss.com/angrysearch/
|
||||
[12]: https://itsfoss.com/searchmonkey-search-text-files-linux/
|
||||
@@ -1,599 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Introduction to the Linux goto shell utility)
|
||||
[#]: via: (https://opensource.com/article/20/1/directories-autocomplete-linux)
|
||||
[#]: author: (Lazarus Lazaridis https://opensource.com/users/iridakos)
|
||||
|
||||
Introduction to the Linux goto shell utility
|
||||
======
|
||||
Learn how to use goto to alias and navigate to directories with
|
||||
autocomplete in Linux.
|
||||
![Files in a folder][1]
|
||||
|
||||
The goto shell utility allows users to navigate to aliased directories and also supports autocompletion.
|
||||
|
||||
## How it works
|
||||
|
||||
Before you can use goto, you need to register your directory aliases. For example:
|
||||
|
||||
|
||||
```
|
||||
`goto -r dev /home/iridakos/development`
|
||||
```
|
||||
|
||||
then change to that directory, e.g.:
|
||||
|
||||
|
||||
```
|
||||
`goto dev`
|
||||
```
|
||||
|
||||
![goto demo][2]
|
||||
|
||||
## Autocompletion in goto
|
||||
|
||||
**goto** comes with a nice autocompletion script—whenever you press the Tab key after the **goto** command, Bash or Zsh will prompt you with suggestions of the aliases that are available:
|
||||
|
||||
|
||||
```
|
||||
$ goto <tab>
|
||||
bc /etc/bash_completion.d
|
||||
dev /home/iridakos/development
|
||||
rubies /home/iridakos/.rvm/rubies
|
||||
```
|
||||
|
||||
## Installing goto
|
||||
|
||||
There are several ways to install goto.
|
||||
|
||||
### Via script
|
||||
|
||||
Clone the repository and run the install script as a superuser or root:
|
||||
|
||||
|
||||
```
|
||||
git clone <https://github.com/iridakos/goto.git>
|
||||
cd goto
|
||||
sudo ./install
|
||||
```
|
||||
|
||||
### Manually
|
||||
|
||||
Copy the file **goto.sh** somewhere in your filesystem and add a line in your **.zshrc** or **.bashrc** to source it.
|
||||
|
||||
For example, if you placed the file in your home folder, all you have to do is add the following line to your **.zshrc** or **.bashrc** file:
|
||||
|
||||
|
||||
```
|
||||
`source ~/goto.sh`
|
||||
```
|
||||
|
||||
### MacOS Homebrew
|
||||
|
||||
A formula named **goto** is available for the Bash shell in MacOS:
|
||||
|
||||
|
||||
```
|
||||
`brew install goto`
|
||||
```
|
||||
|
||||
### Add colored output
|
||||
|
||||
|
||||
```
|
||||
`echo -e "\$include /etc/inputrc\nset colored-completion-prefix on" >> ~/.inputrc`
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
* You need to restart your shell after installation.
|
||||
* You need to have the Bash completion feature enabled for Bash in MacOS (see this [issue][3]).
|
||||
* You can install it with **brew install bash-completion** if you don't have it enabled.
|
||||
|
||||
|
||||
|
||||
## Ways to use goto
|
||||
|
||||
### Change to an aliased directory
|
||||
|
||||
To change to an aliased directory, type:
|
||||
|
||||
|
||||
```
|
||||
`goto <alias>`
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
```
|
||||
`goto dev`
|
||||
```
|
||||
|
||||
### Register an alias
|
||||
|
||||
To register a directory alias, type:
|
||||
|
||||
|
||||
```
|
||||
`goto -r <alias> <directory>`
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
|
||||
```
|
||||
`goto --register <alias> <directory>`
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
```
|
||||
`goto -r blog /mnt/external/projects/html/blog`
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
|
||||
```
|
||||
`goto --register blog /mnt/external/projects/html/blog`
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
* **goto** **expands** the directories, so you can easily alias your current directory with the following command and it will automatically be aliased to the whole path: [code]`goto -r last_release .`
|
||||
```
|
||||
* Pressing the Tab key after the alias name provides the shell's default directory suggestions.
|
||||
|
||||
|
||||
|
||||
### Unregister an alias
|
||||
|
||||
To unregister an alias, use:
|
||||
```
|
||||
`goto -u <alias>`
|
||||
```
|
||||
or
|
||||
```
|
||||
`goto --unregister <alias>`
|
||||
```
|
||||
For example:
|
||||
```
|
||||
`goto -u last_release`
|
||||
```
|
||||
or
|
||||
```
|
||||
`goto --unregister last_release`
|
||||
```
|
||||
**Note:** By pressing the Tab key after the command (**-u** or **\--unregister**), the completion script will prompt you with the list of registered aliases.
|
||||
|
||||
### List aliases
|
||||
|
||||
To get a list of your currently registered aliases, use:
|
||||
```
|
||||
`goto -l`
|
||||
```
|
||||
or
|
||||
```
|
||||
`goto --list`
|
||||
```
|
||||
### Expand an alias
|
||||
|
||||
To expand an alias to its value, use:
|
||||
```
|
||||
`goto -x <alias>`
|
||||
```
|
||||
or
|
||||
```
|
||||
`goto --expand <alias>`
|
||||
```
|
||||
For example:
|
||||
```
|
||||
`goto -x last_release`
|
||||
```
|
||||
or
|
||||
```
|
||||
`goto --expand last_release`
|
||||
```
|
||||
### Clean up aliases
|
||||
|
||||
To clean up the aliases from directories that are no longer accessible in your filesystem, use:
|
||||
```
|
||||
`goto -c`
|
||||
```
|
||||
or
|
||||
```
|
||||
`goto --cleanup`
|
||||
```
|
||||
### Get help
|
||||
|
||||
To view the tool's help information, use:
|
||||
```
|
||||
`goto -h`
|
||||
```
|
||||
or
|
||||
```
|
||||
`goto --help`
|
||||
```
|
||||
### Check the version
|
||||
|
||||
To view the tool's version, use:
|
||||
```
|
||||
`goto -v`
|
||||
```
|
||||
or
|
||||
```
|
||||
`goto --version`
|
||||
```
|
||||
### Push before changing directories
|
||||
|
||||
To push the current directory onto the directory stack before changing directories, type:
|
||||
```
|
||||
`goto -p <alias>`
|
||||
```
|
||||
or
|
||||
```
|
||||
`goto --push <alias>`
|
||||
```
|
||||
### Revert to a pushed directory
|
||||
|
||||
To return to a pushed directory, type:
|
||||
```
|
||||
`goto -o`
|
||||
```
|
||||
or
|
||||
```
|
||||
`goto --pop`
|
||||
```
|
||||
**Note:** This command is equivalent to **popd** but within the **goto** command.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you see the error **command not found: compdef** in Zsh, it means you need to load **bashcompinit**. To do so, append this to your **.zshrc** file:
|
||||
```
|
||||
|
||||
|
||||
autoload bashcompinit
|
||||
bashcompinit
|
||||
|
||||
```
|
||||
## Get involved
|
||||
|
||||
The goto tool is open source under the [MIT License][4] terms, and contributions are welcomed. To learn more, visit the [Contributing][5] section in goto's GitHub repository.
|
||||
|
||||
## The goto script
|
||||
```
|
||||
|
||||
|
||||
goto()
|
||||
{
|
||||
local target
|
||||
_goto_resolve_db
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
# display usage and exit when no args
|
||||
_goto_usage
|
||||
return
|
||||
fi
|
||||
|
||||
subcommand="$1"
|
||||
shift
|
||||
case "$subcommand" in
|
||||
-c|--cleanup)
|
||||
_goto_cleanup "$@"
|
||||
;;
|
||||
-r|--register) # Register an alias
|
||||
_goto_register_alias "$@"
|
||||
;;
|
||||
-u|--unregister) # Unregister an alias
|
||||
_goto_unregister_alias "$@"
|
||||
;;
|
||||
-p|--push) # Push the current directory onto the pushd stack, then goto
|
||||
_goto_directory_push "$@"
|
||||
;;
|
||||
-o|--pop) # Pop the top directory off of the pushd stack, then change that directory
|
||||
_goto_directory_pop
|
||||
;;
|
||||
-l|--list)
|
||||
_goto_list_aliases
|
||||
;;
|
||||
-x|--expand) # Expand an alias
|
||||
_goto_expand_alias "$@"
|
||||
;;
|
||||
-h|--help)
|
||||
_goto_usage
|
||||
;;
|
||||
-v|--version)
|
||||
_goto_version
|
||||
;;
|
||||
*)
|
||||
_goto_directory "$subcommand"
|
||||
;;
|
||||
esac
|
||||
return $?
|
||||
}
|
||||
|
||||
_goto_resolve_db()
|
||||
{
|
||||
GOTO_DB="${GOTO_DB:-$HOME/.goto}"
|
||||
touch -a "$GOTO_DB"
|
||||
}
|
||||
|
||||
_goto_usage()
|
||||
{
|
||||
cat <<\USAGE
|
||||
usage: goto [<option>] <alias> [<directory>]
|
||||
|
||||
default usage:
|
||||
goto <alias> \- changes to the directory registered for the given alias
|
||||
|
||||
OPTIONS:
|
||||
-r, --register: registers an alias
|
||||
goto -r|--register <alias> <directory>
|
||||
-u, --unregister: unregisters an alias
|
||||
goto -u|--unregister <alias>
|
||||
-p, --push: pushes the current directory onto the stack, then performs goto
|
||||
goto -p|--push <alias>
|
||||
-o, --pop: pops the top directory from the stack, then changes to that directory
|
||||
goto -o|--pop
|
||||
-l, --list: lists aliases
|
||||
goto -l|--list
|
||||
-x, --expand: expands an alias
|
||||
goto -x|--expand <alias>
|
||||
-c, --cleanup: cleans up non existent directory aliases
|
||||
goto -c|--cleanup
|
||||
-h, --help: prints this help
|
||||
goto -h|--help
|
||||
-v, --version: displays the version of the goto script
|
||||
goto -v|--version
|
||||
USAGE
|
||||
}
|
||||
|
||||
# Displays version
|
||||
_goto_version()
|
||||
{
|
||||
echo "goto version 1.2.4.1"
|
||||
}
|
||||
|
||||
# Expands directory.
|
||||
# Helpful for ~, ., .. paths
|
||||
_goto_expand_directory()
|
||||
{
|
||||
builtin cd "$1" 2>/dev/null && pwd
|
||||
}
|
||||
|
||||
# Lists registered aliases.
|
||||
_goto_list_aliases()
|
||||
{
|
||||
local IFS=$' '
|
||||
if [ -f "$GOTO_DB" ]; then
|
||||
while read -r name directory; do
|
||||
printf '\e[1;36m%20s \e[0m%s\n' "$name" "$directory"
|
||||
done < "$GOTO_DB"
|
||||
else
|
||||
echo "You haven't configured any directory aliases yet."
|
||||
fi
|
||||
}
|
||||
|
||||
# Expands a registered alias.
|
||||
_goto_expand_alias()
|
||||
{
|
||||
if [ "$#" -ne "1" ]; then
|
||||
_goto_error "usage: goto -x|--expand <alias>"
|
||||
return
|
||||
fi
|
||||
|
||||
local resolved
|
||||
|
||||
resolved=$(_goto_find_alias_directory "$1")
|
||||
if [ -z "$resolved" ]; then
|
||||
_goto_error "alias '$1' does not exist"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "$resolved"
|
||||
}
|
||||
|
||||
# Lists duplicate directory aliases
|
||||
_goto_find_duplicate()
|
||||
{
|
||||
local duplicates=
|
||||
|
||||
duplicates=$(sed -n 's:[^ ]* '"$1"'$:&:p' "$GOTO_DB" 2>/dev/null)
|
||||
echo "$duplicates"
|
||||
}
|
||||
|
||||
# Registers and alias.
|
||||
_goto_register_alias()
|
||||
{
|
||||
if [ "$#" -ne "2" ]; then
|
||||
_goto_error "usage: goto -r|--register <alias> <directory>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! [[ $1 =~ ^[[:alnum:]]+[a-zA-Z0-9_-]*$ ]]; then
|
||||
_goto_error "invalid alias - can start with letters or digits followed by letters, digits, hyphens or underscores"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local resolved
|
||||
resolved=$(_goto_find_alias_directory "$1")
|
||||
|
||||
if [ -n "$resolved" ]; then
|
||||
_goto_error "alias '$1' exists"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local directory
|
||||
directory=$(_goto_expand_directory "$2")
|
||||
if [ -z "$directory" ]; then
|
||||
_goto_error "failed to register '$1' to '$2' - can't cd to directory"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local duplicate
|
||||
duplicate=$(_goto_find_duplicate "$directory")
|
||||
if [ -n "$duplicate" ]; then
|
||||
_goto_warning "duplicate alias(es) found: \\\n$duplicate"
|
||||
fi
|
||||
|
||||
# Append entry to file.
|
||||
echo "$1 $directory" >> "$GOTO_DB"
|
||||
echo "Alias '$1' registered successfully."
|
||||
}
|
||||
|
||||
# Unregisters the given alias.
|
||||
_goto_unregister_alias()
|
||||
{
|
||||
if [ "$#" -ne "1" ]; then
|
||||
_goto_error "usage: goto -u|--unregister <alias>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local resolved
|
||||
resolved=$(_goto_find_alias_directory "$1")
|
||||
if [ -z "$resolved" ]; then
|
||||
_goto_error "alias '$1' does not exist"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
local readonly GOTO_DB_TMP="$HOME/.goto_"
|
||||
# Delete entry from file.
|
||||
sed "/^$1 /d" "$GOTO_DB" > "$GOTO_DB_TMP" && mv "$GOTO_DB_TMP" "$GOTO_DB"
|
||||
echo "Alias '$1' unregistered successfully."
|
||||
}
|
||||
|
||||
# Pushes the current directory onto the stack, then goto
|
||||
_goto_directory_push()
|
||||
{
|
||||
if [ "$#" -ne "1" ]; then
|
||||
_goto_error "usage: goto -p|--push <alias>"
|
||||
return
|
||||
fi
|
||||
|
||||
{ pushd . || return; } 1>/dev/null 2>&1
|
||||
|
||||
_goto_directory "$@"
|
||||
}
|
||||
|
||||
# Pops the top directory from the stack, then goto
|
||||
_goto_directory_pop()
|
||||
{
|
||||
{ popd || return; } 1>/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Unregisters aliases whose directories no longer exist.
|
||||
_goto_cleanup()
|
||||
{
|
||||
if ! [ -f "$GOTO_DB" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
while IFS= read -r i && [ -n "$i" ]; do
|
||||
echo "Cleaning up: $i"
|
||||
_goto_unregister_alias "$i"
|
||||
done <<< "$(awk '{al=$1; $1=""; dir=substr($0,2);
|
||||
system("[ ! -d \"" dir "\" ] && echo " al)}' "$GOTO_DB")"
|
||||
}
|
||||
|
||||
# Changes to the given alias' directory
|
||||
_goto_directory()
|
||||
{
|
||||
local target
|
||||
|
||||
target=$(_goto_resolve_alias "$1") || return 1
|
||||
|
||||
builtin cd "$target" 2> /dev/null || \
|
||||
{ _goto_error "Failed to goto '$target'" && return 1; }
|
||||
}
|
||||
|
||||
# Fetches the alias directory.
|
||||
_goto_find_alias_directory()
|
||||
{
|
||||
local resolved
|
||||
|
||||
resolved=$(sed -n "s/^$1 \\\\(.*\\\\)/\\\1/p" "$GOTO_DB" 2>/dev/null)
|
||||
echo "$resolved"
|
||||
}
|
||||
|
||||
# Displays the given error.
|
||||
# Used for common error output.
|
||||
_goto_error()
|
||||
{
|
||||
(>&2 echo -e "goto error: $1")
|
||||
}
|
||||
|
||||
# Displays the given warning.
|
||||
# Used for common warning output.
|
||||
_goto_warning()
|
||||
{
|
||||
(>&2 echo -e "goto warning: $1")
|
||||
}
|
||||
|
||||
# Displays entries with aliases starting as the given one.
|
||||
_goto_print_similar()
|
||||
{
|
||||
local similar
|
||||
|
||||
similar=$(sed -n "/^$1[^ ]* .*/p" "$GOTO_DB" 2>/dev/null)
|
||||
if [ -n "$similar" ]; then
|
||||
(>&2 echo "Did you mean:")
|
||||
(>&2 column -t <<< "$similar")
|
||||
fi
|
||||
}
|
||||
|
||||
# Fetches alias directory, errors if it doesn't exist.
|
||||
_goto_resolve_alias()
|
||||
{
|
||||
local resolved
|
||||
|
||||
resolved=$(_goto_find_alias_directory "$1")
|
||||
|
||||
if [ -z "$resolved" ]; then
|
||||
_goto_error "unregistered alias $1"
|
||||
_goto_print_similar "$1"
|
||||
return 1
|
||||
else
|
||||
echo "${resolved}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Completes the goto function with the available commands
|
||||
_complete_goto_commands()
|
||||
{
|
||||
local IFS=$' \t\n'
|
||||
|
||||
# shellcheck disable=SC2207
|
||||
COMPREPLY=($(compgen -W "-r --register -u --unregister -p --push -o --pop -l --list -x --expand -c --cleanup -v --version" -- "$1"))
|
||||
}
|
||||
|
||||
# Completes the goto function with the available aliases
|
||||
_complete_goto_aliases()
|
||||
{
|
||||
local IFS=$'\n' matches
|
||||
_goto_resolve_db
|
||||
|
||||
# shellcheck disable=SC2207
|
||||
matches=($(sed -n "/^$1/p" "$GOTO_DB" 2>/dev/null))
|
||||
|
||||
if [ "${#matches[@]}" -eq "1" ]; then
|
||||
# remove the filenames attribute from the completion method
|
||||
compopt +o filenames 2>/dev/null
|
||||
|
||||
# if you find only one alias don't append the directory
|
||||
COMPREPLY=("${matches[0]// *}")
|
||||
else
|
||||
for i in "${!matches[@]}"; do
|
||||
# remove the filenames attribute from the completion method
|
||||
compopt +o filenames 2>/dev/null
|
||||
|
||||
if ! [[ $(uname -s) =~ Darwin* ]]; then
|
||||
matches[$i]=$(printf '%*s' "-$COLUMNS" "${matches[$i]}")
|
||||
|
||||
COMPREPLY+=("$(compgen -W "${matches[$i]}")")
|
||||
els
|
||||
@@ -1,232 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Automating the creation of research artifacts)
|
||||
[#]: via: (https://opensource.com/article/20/1/automating-documentation)
|
||||
[#]: author: (Kiko Fernandez-Reyes https://opensource.com/users/kikofernandez)
|
||||
|
||||
Automating the creation of research artifacts
|
||||
======
|
||||
A simple way to automate generating source code documentation, creating
|
||||
HTML and PDF versions of user documentation, compiling a technical
|
||||
(research) document to PDF, generating the bibliography, and
|
||||
provisioning virtual machines.
|
||||
![Files in a folder][1]
|
||||
|
||||
In my work as a programming language researcher, I need to create [artifacts][2] that are easy to understand and well-documented. To make my work easier, I found a simple way to automate generating source code documentation, creating HTML and PDF versions of user documentation, compiling a technical (research) document to PDF, generating the bibliography, and provisioning of virtual machines with the software artefact installed for ease of reproducibility of my research.
|
||||
|
||||
The tools I use are:
|
||||
|
||||
* [Make][3] makefiles for overall orchestration of all components
|
||||
* [Haddock][4] for generating source code documentation
|
||||
* [Pandoc][5] for generating PDF and HTML files from a Markdown file
|
||||
* [Vagrant][6] for provisioning virtual machines
|
||||
* [Stack][7] for downloading Haskell dependencies, compiling, running tests, etc
|
||||
* [pdflaTeX][8] for compiling a LaTeX file to PDF format
|
||||
* [BibTeX][9] for generating a bibliography
|
||||
* [Zip][10] to pack everything and get it ready for distribution
|
||||
|
||||
|
||||
|
||||
I use the following folder and file structure:
|
||||
|
||||
|
||||
```
|
||||
├── Makefile
|
||||
├── Vagrantfile
|
||||
├── code
|
||||
│ └── typechecker-oopl (Project)
|
||||
│ ├── Makefile
|
||||
│ └── ...
|
||||
│
|
||||
├── documentation
|
||||
│ ├── Makefile
|
||||
│ ├── README.md
|
||||
│ ├── assets
|
||||
│ │ ├── pandoc.css (Customised CSS for Pandoc)
|
||||
│ │ └── submitted-version.pdf (PDF of your research)
|
||||
│ └── meta.yaml
|
||||
│
|
||||
├── research
|
||||
│ ├── Makefile
|
||||
│ ├── ACM-Reference-Format.bst
|
||||
│ ├── acmart.cls
|
||||
│ ├── biblio.bib
|
||||
│ └── typecheckingMonad.tex
|
||||
```
|
||||
|
||||
The Makefile glues together the output from all of the tools listed above. The **code** folder contains the source code of the tool/language I created. The **documentation** folder contains a Makefile that has instructions on how to generate PDF and HTML versions of the user instructions, located in the README.md file. I generate the PDF and HTML user documentation using Pandoc. The **assets** are simply the CSS style to use and a PDF of my research article that will be hyperlinked from the user-generated documentation, so that it is easy to follow. **meta.yaml** contains meta instructions for generating the user documentation, used by Pandoc for e.g., for author names. The **research** folder contains my research article in LaTeX format, but it could hold any other technical document.
|
||||
|
||||
As you can see in the structure, I have a [Makefile][11] for each folder to decouple each Makefile's responsibility and keep a (somewhat) maintainable design. Here is an overview of the top-level Makefile, which orchestrates generating the user documentation, research paper, bibliography, documentation from source code, and provisioning of a virtual machine.
|
||||
|
||||
|
||||
```
|
||||
all: doc gen
|
||||
|
||||
doc:
|
||||
make -C $(DOC_SRC) $@
|
||||
make -C $(CODE_PATH) $@
|
||||
make -C $(RESEARCH)
|
||||
|
||||
gen:
|
||||
# Creation of folder with artefact, empty at the moment
|
||||
mkdir -p $(ARTEFACT_FOLDER)
|
||||
|
||||
# Moving user documentation to artefact folder
|
||||
cp $(DOC_SRC)/$(README).pdf $(ARTEFACT_FOLDER)
|
||||
cp $(DOC_SRC)/$(README).html $(ARTEFACT_FOLDER)
|
||||
cp -r $(DOC_SRC)/$(ASSETS) $(ARTEFACT_FOLDER)
|
||||
|
||||
# Moving research article to artefact folder
|
||||
cp $(RESEARCH)/$(RESEARCH_PAPER).pdf $(ARTEFACT_FOLDER)/$(ASSETS)/submitted-version.pdf
|
||||
|
||||
# Moving code and autogenerated doc to artefact folder
|
||||
cp -r $(CODE_PATH) $(ARTEFACT_FOLDER)
|
||||
cd $(ARTEFACT_FOLDER)/$(CODE_SRC)
|
||||
$(STACK)
|
||||
cd ../..
|
||||
rm -rf $(ARTEFACT_FOLDER)/$(DOC_SRC)
|
||||
mv $(ARTEFACT_FOLDER)/$(CODE_SRC)/$(HADDOCK) $(ARTEFACT_FOLDER)/$(DOC_SRC)
|
||||
|
||||
# zip it!
|
||||
zip $(ZIP_FILE) $(ARTEFACT_FOLDER)
|
||||
|
||||
update:
|
||||
vagrant up
|
||||
vagrant provision
|
||||
|
||||
clean:
|
||||
rm -rf $(ARTEFACT_FOLDER)
|
||||
|
||||
.PHONY: all clean doc gen update
|
||||
```
|
||||
|
||||
First, the **doc** target generates the user documentation using Pandoc, then it uses Haddock to generate the documentation from the Haskell library source code, and finally, it creates a PDF from the LaTeX file. As depicted in the image below, the generated user documentation is in HTML and CSS. The user documentation contains links to the generated source code documentation, also in HTML and CSS, and to the technical (research) paper . The generated source code documentation links directly to the source code, in case the reader would like to understand the implementation.
|
||||
|
||||
![Artifact automation structure][12]
|
||||
|
||||
The user documentation is generated with the following Makefile:
|
||||
|
||||
|
||||
```
|
||||
DOCS=README.md
|
||||
META=meta.yaml
|
||||
NUMBER_SECTION_HEADINGS=-N
|
||||
|
||||
.PHONY: all doc clean
|
||||
|
||||
all: doc
|
||||
|
||||
doc: $(DOC)
|
||||
pandoc -s $(META) $(DOCS) --listings --pdf-engine=xelatex -c assets/pandoc.css -o $(DOCS:md=pdf)
|
||||
pandoc -s $(META) $(DOCS) --self-contained -c assets/pandoc.css -o $(DOCS:md=html)
|
||||
|
||||
clean:
|
||||
rm $(DOCS:md=pdf) $(DOCS:md=html)
|
||||
```
|
||||
|
||||
To generate documentation from Haskell code, I use this other Makefile, which makes use of Stack to compile the library and download dependencies, and Haddock (inside its OPTS, or options) to create documentation in HMTL:
|
||||
|
||||
|
||||
```
|
||||
OPTS=exec -- haddock --html --hyperlinked-source --odir=docs
|
||||
|
||||
doc:
|
||||
stack $(OPTS) src/Initial/AST.hs src/Initial/Typechecker.hs \
|
||||
src/Reader/AST.hs src/Reader/Typechecker.hs \
|
||||
src/Backtrace/AST.hs src/Backtrace/Typechecker.hs \
|
||||
src/Warning/AST.hs src/Warning/Typechecker.hs \
|
||||
src/MultiError/AST.hs src/MultiError/Typechecker.hs \
|
||||
src/PhantomFunctors/AST.hs src/PhantomFunctors/Typechecker.hs \
|
||||
src/PhantomPhases/AST.hs src/PhantomPhases/Typechecker.hs \
|
||||
src/Applicative/AST.hs src/Applicative/Typechecker.hs \
|
||||
src/Final/AST.hs src/Final/Typechecker.hs
|
||||
|
||||
.PHONY: doc
|
||||
```
|
||||
|
||||
I compile the research paper from LaTeX to PDF with this simple Makefile:
|
||||
|
||||
|
||||
```
|
||||
.PHONY: research
|
||||
|
||||
research:
|
||||
pdflatex typecheckingMonad.tex
|
||||
bibtex typecheckingMonad
|
||||
pdflatex typecheckingMonad.tex
|
||||
pdflatex typecheckingMonad.tex
|
||||
```
|
||||
|
||||
The virtual machine (VM) relies on Vagrant and the Vagrantfile, where I can write all the commands to set up the VM. The one thing that I do not know how to automate is moving all of the documentation, once it is generated, into the VM. If you know how to transfer the file from the host machine to the VM, please share your solution in the comments. That means that, currently, I manually enter in the VM and place the documentation in the Desktop folder.
|
||||
|
||||
|
||||
```
|
||||
# All Vagrant configuration is done below. The "2" in Vagrant.configure
|
||||
# configures the configuration version (we support older styles for
|
||||
# backwards compatibility). Please don't change it unless you know what
|
||||
# you're doing.
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "ubuntu/trusty64"
|
||||
config.ssh.username = "vagrant"
|
||||
config.ssh.password = "vagrant"
|
||||
config.vm.provider "virtualbox" do |vb|
|
||||
# Display the VirtualBox GUI when booting the machine
|
||||
vb.gui = true
|
||||
|
||||
# Customize the amount of memory on the VM:
|
||||
vb.memory = "2048"
|
||||
vb.customize ["modifyvm", :id, "--vram", "64"]
|
||||
end
|
||||
config.vm.provision "shell", inline: <<-SHELL
|
||||
## Installing dependencies, comment after this has been done once.
|
||||
# sudo apt-get update -y
|
||||
# sudo apt-get install ubuntu-desktop -y
|
||||
# sudo apt-get install -y build-essential linux-headers-server
|
||||
|
||||
# echo 'PATH="/home/vagrant/.local/bin:$PATH"' >> /home/vagrant/.profile
|
||||
|
||||
## Comment and remove the folder sharing before submission
|
||||
mkdir -p /home/vagrant/Desktop/TypeChecker
|
||||
cp -r /vagrant/artefact-submission/* /home/vagrant/Desktop/TypeChecker/
|
||||
chown -R vagrant:vagrant /home/vagrant/Desktop/TypeChecker/
|
||||
SHELL
|
||||
end
|
||||
```
|
||||
|
||||
With this final step, everything has been wired. You can see one example of the result [in HTML][13] and [in PDF][14]. I have created a [GitHub repo with all the source code][15] for ease of study and reproducibility.
|
||||
|
||||
I have used this setup for two conferences—the European Conference on Object-Oriented Programming (ECOOP) and the International Conference on Software Language Engineering (SLE), where we won (in both) the Disguinshed Artifact Award.
|
||||
|
||||
Pinterest software engineer Baogang Song tells us about Pinrepo, Pinterest's open source solution...
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/automating-documentation
|
||||
|
||||
作者:[Kiko Fernandez-Reyes][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/kikofernandez
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/files_documents_paper_folder.png?itok=eIJWac15 (Files in a folder)
|
||||
[2]: https://en.wikipedia.org/wiki/Artifact_%28software_development%29
|
||||
[3]: https://en.wikipedia.org/wiki/Make_%28software%29
|
||||
[4]: https://www.haskell.org/haddock/
|
||||
[5]: https://pandoc.org/
|
||||
[6]: https://www.vagrantup.com/
|
||||
[7]: https://docs.haskellstack.org/en/stable/README/
|
||||
[8]: https://linux.die.net/man/1/pdflatex
|
||||
[9]: http://www.bibtex.org/
|
||||
[10]: https://linux.die.net/man/1/zip
|
||||
[11]: https://opensource.com/article/18/8/what-how-makefile
|
||||
[12]: https://opensource.com/sites/default/files/uploads/makefile_pandoc_haddock.png (Artifact automation structure)
|
||||
[13]: https://www.plresearcher.com/files/monadic-typechecker/README.html
|
||||
[14]: https://www.plresearcher.com/files/monadic-typechecker/README.pdf
|
||||
[15]: https://github.com/kikofernandez/pandoc-examples/tree/master/artefact-creation
|
||||
@@ -1,70 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Detecting CPU steal time in guest virtual machines)
|
||||
[#]: via: (https://opensource.com/article/20/1/cpu-steal-time)
|
||||
[#]: author: (Jamie Fargen https://opensource.com/users/jamiefargen)
|
||||
|
||||
Detecting CPU steal time in guest virtual machines
|
||||
======
|
||||
Is your VM getting all of its vitamin CPU? Use GNU top to find out
|
||||
what's causing guest performance issues.
|
||||
![and old computer and a new computer, representing migration to new software or hardware][1]
|
||||
|
||||
CPU steal time is defined in the [GNU **top**][2] command as "time stolen from [a] VM by the hypervisor." CPU steal time occurs when a hypervisor process and a guest instance are trying to utilize the same hypervisor physical core (pCPU) at the same time. This results in less processor time available to the guest's virtual CPU (vCPU) and performance degradation for the guest.
|
||||
|
||||
In today's virtualized environments (which have become nearly universal with the adoption of public and private clouds), a guest instance can experience performance CPU steal time under several scenarios:
|
||||
|
||||
* Oversubscription of the hypervisor and multiple guest VMs' vCPUs with high CPU utilization are running on the same pCPUs.
|
||||
* The guest vCPU and its emulator thread are pinned to the same pCPU resulting in vhost processes stealing CPU time from the guest vCPU while processing I/O.
|
||||
* Hypervisor processes, like monitoring, logging, and I/O processes, are concurrently using a pCPU that is also in use by a guest VM vCPU.
|
||||
|
||||
|
||||
|
||||
Normally, a systems engineer brought in to investigate an application or system performance issue will find that the system's performance is degraded due to CPU time stolen from the guest. The guest's performance issues usually become apparent in the form of low disk or network I/O performance, network packet loss, and other application performance anomalies.
|
||||
|
||||
Even when a system administrator is observing the guest and the hypervisor, it can be difficult to narrow down the cause of the guest instance's degraded performance due to CPU steal time. There are a few reasons for the difficulty. First, CPU steal time is not logged by any of the commonly monitored log files. A hypervisor that is being observed may be expected to be under heavy load but steal time can occur on hypervisors that are under normal load. And finally, administrators may not be aware that hypervisor CPU contention can be observed from within the guest VM instance using GNU top.
|
||||
|
||||
Fortunately, GNU top indeed makes it quite easy to detect CPU steal time on a guest VM instance. Steal time is displayed in top's output at the end of line 3, which beings with **%Cpu(s)**, as shown in the following screenshots (it is the value at the end, labeled **st**.) The first example shows a guest with little steal time:
|
||||
|
||||
![Output of the top command showing low CPU steal time][3]
|
||||
|
||||
Output of the top command from a guest experiencing a low CPU steal time of 0.2 st.
|
||||
|
||||
This screenshot shows a guest experiencing heavy CPU steal time:
|
||||
|
||||
![Output of the top command showing high CPU steal time][4]
|
||||
|
||||
Output of the top command from a guest experiencing a heavy CPU steal time of 9.0 st.
|
||||
|
||||
In both examples, the stress tool was executed with four processes that consumed all four vCPUs of the guest instance. In the first example, the hypervisor was relatively idle, so the guest's steal time was just 0.2. But in the second example, the stress tool was executed at the same time on the hypervisor with eight processes that consumed all eight of the hypervisors' pCPUs, which produced a high CPU steal time of 9.0.
|
||||
|
||||
There is another sign of steal time in the second example: the stress utility process cannot consume ~100% of the guests' vCPUs; it can only consume 99.3%, 99.3%, 86.4%, and 74.4%, respectively. In total, this is equal to 40.3% of a guest vCPU's being stolen. This is because the hypervisor is consuming cycles on the same pCPUs that the guest vCPU's processes are using.
|
||||
|
||||
### Using top to mitigate poor performance
|
||||
|
||||
This example shows how the oversubscription of guest VM instances and other processes on a hypervisor can contend with a guest, and how to use GNU top to detect it based on the CPU steal time percentage on a guest VM.
|
||||
|
||||
It is important to detect this type of performance degradation in a guest VM so that you can mitigate the cause of poor system and application performance. In a public cloud, the only solution might be migrating the instance or changing to an instance type with guaranteed pCPU service-level agreements (SLAs). In a private cloud, there are more options, but again, the simplest approach may be to migrate the instance to a hypervisor with lower utilization. However, if many guest instances experience high CPU steal time, you will need to make changes to how guests' and hypervisors' processes are managed to attain guest instances' performance SLAs.
|
||||
|
||||
David Both explains the importance of keeping hardware cool and shares some Linux tools that can...
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/cpu-steal-time
|
||||
|
||||
作者:[Jamie Fargen][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jamiefargen
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/migration_innovation_computer_software.png?itok=VCFLtd0q (and old computer and a new computer, representing migration to new software or hardware)
|
||||
[2]: https://en.wikipedia.org/wiki/Top_(software)
|
||||
[3]: https://opensource.com/sites/default/files/uploads/cpu-steal-time_1.png (Output of the top command showing low CPU steal time)
|
||||
[4]: https://opensource.com/sites/default/files/uploads/cpu-steal-time_2.png (Output of the top command showing high CPU steal time)
|
||||
@@ -1,130 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Create demo project templates with one script)
|
||||
[#]: via: (https://opensource.com/article/20/1/initsh-script)
|
||||
[#]: author: (Eric D. Schabell https://opensource.com/users/eschabell)
|
||||
|
||||
Create demo project templates with one script
|
||||
======
|
||||
This init.sh script makes it easier to create demos to convince your
|
||||
customers, team, and boss that your project is exactly what they need.
|
||||
![Person using a laptop][1]
|
||||
|
||||
When you're standing on a stage or doing a live demo in an online session, getting your project into a perfect-looking state may appear easy. But a lot of work goes on behind the scenes to create working, easy to use, and repeatable demo projects.
|
||||
|
||||
When you're doing a demo, the technology in a project must support your bigger story about the project without failing. My fellow JBoss technology evangelists and I often have to set up different technologies, so it became necessary for us to tune some sort of generic framework or template to put these demo projects into.
|
||||
|
||||
Achieving this goal was guided by three principles:
|
||||
|
||||
* KISS (keep it simple, stupid)
|
||||
* Consistency
|
||||
* Repeatability
|
||||
|
||||
|
||||
|
||||
These stem from our aim to support anyone who wants to explore and get started with a JBoss technology. Note that while the order of these principles has some significance about what is most important, they all have to balance each other.
|
||||
|
||||
If one must be weighed more heavily, when using technology, it is KISS. Therefore, you will find that this principle is followed almost religiously when deciding how to solve anything within the demo project template.
|
||||
|
||||
### Simple
|
||||
|
||||
The demos need to have a simple set up, taking almost no effort to get them started or, more realistically, the least amount of effort possible.
|
||||
|
||||
Each project requires just three steps to get going:
|
||||
|
||||
1. Download and unzip the project.
|
||||
2. Add products to the project's **installs** directory.
|
||||
3. Run **init.sh** (for Unix) or **init.bat** (for Windows) to install the project.
|
||||
|
||||
|
||||
|
||||
That's it; just watch the install's output to continue with the project as you see fit.
|
||||
|
||||
This setup also relies on the very smallest or most basic set of dependencies that are physically possible for Unix- and Windows-based systems—Java and Maven, nothing more than that.
|
||||
|
||||
A demo is all about learning what the project in front of you does, so it must be clear and offer the possibility to explore how the project is set up, configured, and runs. Based on users' feedback over the years, full automation is not desirable, as it takes the project out of the user's learning sphere.
|
||||
|
||||
There is a balance offered when installing a project with autoconfiguration and setup, but there are still some steps that allow you to pause, consider, and explore what has been done.
|
||||
|
||||
### Consistent
|
||||
|
||||
The only way for people to be able to jump between our various technologies and products is to have some consistency in our demo projects. A customer, partner, or interested party needs to be able to recognize a simple and clear form of project setup.
|
||||
|
||||
This is done with a simple and clear project template structure:
|
||||
|
||||
* **docs/** contains project documentation and screenshots.
|
||||
* **installs/** is where you place needed products.
|
||||
* **projects/** contains sources for services, clients, or other project-based code.
|
||||
* **support/** holds all other configuration, setup, or other necessary files and utilities.
|
||||
* **init.{sh|bat}** stores installation scripts needed to set up the project.
|
||||
|
||||
|
||||
|
||||
That's it; every single time it will look like this.
|
||||
|
||||
### Repeatable
|
||||
|
||||
Nothing is worse than spending a large amount of time to provide a demo project and only being able to run it one time. Often, there is some factor that rushes a project's completion so that you have little chance to bring some sort of order to it.
|
||||
|
||||
Our template allows you to develop a demo project while saving its repeatability. Note that the entire setup is done in a new directory called **target**, where you can throw it all away and just set the project up again (with the initial **init** script).
|
||||
|
||||
This is a golden rule: Every demo project should be repeatable in minutes, if not less.
|
||||
|
||||
### Generic demo template project
|
||||
|
||||
You want to create your own awesome demo to convince your friends, team, and boss that your project is exactly what the doctor ordered? This template will provide the tools to set up simple, consistent and easily repeatable demo projects.
|
||||
|
||||
### Getting started with this template
|
||||
|
||||
1. [Download and unzip it][2].
|
||||
|
||||
|
||||
|
||||
Run **init.sh** to populate a project, and see the README files that are generated for how to use it:
|
||||
|
||||
|
||||
```
|
||||
`$ ./init.sh PROJECTNAME`
|
||||
```
|
||||
|
||||
####
|
||||
|
||||
![Setting up your project][3]
|
||||
|
||||
#### Released versions
|
||||
|
||||
* v2.0—Updated to generic template project creation
|
||||
* v1.0—Image added and final touches
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
![Installing the template][4]
|
||||
|
||||
![Installing the template][5]
|
||||
|
||||
_This article is adapted from "How to create simple, consistent, repeatable demo projects" on [Eric D. Schabell's blog][6] and is reused with permission._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/initsh-script
|
||||
|
||||
作者:[Eric D. Schabell][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/eschabell
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/laptop_screen_desk_work_chat_text.png?itok=UXqIDRDD (Person using a laptop)
|
||||
[2]: https://github.com/jbossdemocentral/jboss-demo-template/archive/master.zip
|
||||
[3]: https://opensource.com/sites/default/files/uploads/settingup_demotemplate.png (Setting up your project)
|
||||
[4]: https://opensource.com/sites/default/files/uploads/install_eric_schabell.png (Installing the template)
|
||||
[5]: https://opensource.com/sites/default/files/uploads/install-2_eric_schabell.png (Installing the template)
|
||||
[6]: http://www.schabell.org/2015/02/jboss-evangelist-howto-create-demo-projects.html
|
||||
@@ -1,237 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How I upgraded my CuBox open source music server)
|
||||
[#]: via: (https://opensource.com/article/20/1/upgrade-cubox-open-source-music-server)
|
||||
[#]: author: (Chris Hermansen https://opensource.com/users/clhermansen)
|
||||
|
||||
How I upgraded my CuBox open source music server
|
||||
======
|
||||
Our resident audiophile explains how he updated his CuBox music server
|
||||
after more than five years of excellent use.
|
||||
![open source music with piano keys background][1]
|
||||
|
||||
Back in early 2014, I purchased my first [ARM-based computer][2], a [SolidRun CuBox-i4][3]. My goal for the CuBox was to have a headless device (e.g., no display) that takes up minimal space in the audio equipment shelf, makes minimal noise, and serves music files to my digital-to-analog converter (DAC) and thus to the rest of the stereo. On paper, the CuBox was a perfect fit. It is fanless, works with various Linux distros, provides a separate [eSATA interface][4] for a locally mounted hard drive and therefore leaves the USB ports free for other stuff, provides a [TOSLINK port][5] for digital audio, uses a [MicroSD card][6] (by default, anyway) for the system installation and boot drive, and comes with 2GB of RAM and a reasonably fast [i.MX6q quad-core processor][7], all in an unobtrusive 5x5x5cm black plastic box.
|
||||
|
||||
And as luck would (sometimes) have it, those promises have mostly been kept. The box still runs and provides my family with a great deal of musical enjoyment. The attached hard drive contains about 275GB of music files—a bit over 7,500 FLAC files with some 300 or so MP3 files thrown in.
|
||||
|
||||
As our home music server, the CuBox runs a barebones Linux server distro with Music Player Daemon ([MPD][8]) as the music-serving software, sending digital data to the DAC via the [ALSA][9] with no software mixing, up- or down-sampling, or volume control on the server. Lately, I've found it convenient to run [lighttpd][10], a lightweight, simple-to-configure HTTP server, to provide cover images to the various MPD clients that we run in the house. (But now MPD provides that capability, so that convenience may soon go away.) For the user interfaces to play music, we use [Cantata][11] on the desktop and [MPD Remote][12] (caution: it may not be open source) on our Android phones to browse, select, and play music.
|
||||
|
||||
In 2017, I wrote about [my experiences using the CuBox][13]. I had tried [Voyage Linux][14], [Volumio][15], and [Archphile Linux][16], and had just moved to [Armbian][17], which at that time was based on Ubuntu 16.04 LTS (long term support). This fall, I decided it was time to upgrade the server, get the latest version of MPD, and deal with a few other bothersome details along the way.
|
||||
|
||||
### The upgrade
|
||||
|
||||
Given my generally great experience with Armbian, I decided to go there again for upgrade materials. There are two supported options: a Debian Buster-based server configuration and an Ubuntu 18.04 desktop configuration. Bearing in mind my desire to keep the system display-free and minimalist, I selected the Debian version.
|
||||
|
||||
![Armbian versions][18]
|
||||
|
||||
The download was trouble-free, and I soon had an install image in my Downloads folder. As before, I was impressed with the quality and "to the point" nature of the [Armbian documentation][19], which notes:
|
||||
|
||||
> Images shall only be written with Etcher on all platforms since, unlike other tools, Etcher validates burning results **saving you from corrupted SD card contents**.
|
||||
|
||||
This was my first experience using [Etcher][20], and I was duly impressed with its usability (yet another wonderful reason to use open source software). I inserted the resulting MicroSD card into the CuBox, connected the Ethernet cable, DAC, and eSATA drive, and powered it on. Soon, I could SSH into the CuBox to configure it. Talk about a painless experience! Here's what I see when I log into that machine using SSH:
|
||||
|
||||
![CuBox login][21]
|
||||
|
||||
In terms of configuration, I had the following objectives:
|
||||
|
||||
* Finish the initial configuration
|
||||
* Configure static IP addressing for the wired networking connection
|
||||
* Get the installed system up to date
|
||||
* Disable the wireless
|
||||
* Install and configure MPD
|
||||
* Install and configure lighttpd
|
||||
* Listen to some music!
|
||||
|
||||
|
||||
|
||||
I used the excellent [Armbian Config program][22] to carry out a bunch of these initial configuration tasks.
|
||||
|
||||
![Armbian-Config screen][23]
|
||||
|
||||
Choosing System presents this menu:
|
||||
|
||||
![Armbian-Config settings][24]
|
||||
|
||||
I used this to disable root login over SSH and enable password and public key authentication.
|
||||
|
||||
In the Network subsystem, I set static IP addressing (to conform to my home router settings), and in the Personal subsystem, I changed the time zone and hostname. I probably could have used the Software subsystem to run the initial update, but I chose to exit to the shell and use apt to finish up this initial configuration with:
|
||||
|
||||
|
||||
```
|
||||
sudo apt update
|
||||
sudo apt upgrade
|
||||
```
|
||||
|
||||
I also used apt to install MPD and lighttpd with:
|
||||
|
||||
|
||||
```
|
||||
`sudo apt install mpd lighttpd`
|
||||
```
|
||||
|
||||
I had my previous MPD configuration file (**/etc/mpd.conf**); generally, I prefer to use these types of older files as guides to configure a new one, rather than just plunking the old one on top of the new. In the end, there weren't many changes to make; here are the differences between the old and new files:
|
||||
|
||||
|
||||
```
|
||||
clh@stereo:/etc$ diff mpd.conf.orig mpd.conf
|
||||
85c85,86
|
||||
< bind_to_address "localhost"
|
||||
\---
|
||||
> #bind_to_address "localhost"
|
||||
> bind_to_address "192.168.1.21"
|
||||
241c242,249
|
||||
< name "My ALSA Device"
|
||||
\---
|
||||
> name "Drop Airist R2R"
|
||||
> device "hw:CARD=U20,DEV=0"
|
||||
> mixer_type "none"
|
||||
> replay_gain_handler "none"
|
||||
> # name "Schit Bifrost 4490"
|
||||
> # device "hw:CARD=S2,DEV=0"
|
||||
> # mixer_type "none"
|
||||
> # replay_gain_handler "none"
|
||||
clh@stereo:/etc$
|
||||
```
|
||||
|
||||
You can see that I have two DACs (just because), and the second is commented out (and not connected). Also, I had to change **bind_to_address** so that MPD will listen over the local area network.
|
||||
|
||||
As for lighttpd, I only had to change the **document-root**:
|
||||
|
||||
|
||||
```
|
||||
clh@stereo:/etc/lighttpd$ diff lighttpd.conf.orig lighttpd.conf
|
||||
8c8,13
|
||||
< server.document-root = "/var/www/html"
|
||||
\---
|
||||
> # Used this config documentation
|
||||
> #<https://github.com/abarisain/dmix/wiki/Album-Art-on-your-LAN>
|
||||
> # which wasn't good - wrong user name, didn't need to add
|
||||
> # the mime config
|
||||
> # in the end just changed the document root.
|
||||
>
|
||||
> server.document-root = "/var/lib/mpd/music/"
|
||||
clh@stereo:/etc/lighttpd$
|
||||
```
|
||||
|
||||
To get to the music files, I added the following line to **/etc/fstab**:
|
||||
|
||||
|
||||
```
|
||||
`/dev/sda1 /mnt/eSATA ext4 rw 0 0`
|
||||
```
|
||||
|
||||
and created the mount point with:
|
||||
|
||||
|
||||
```
|
||||
`sudo mkdir /mnt/eSATA`
|
||||
```
|
||||
|
||||
At this point, I mounted the external hard drive with:
|
||||
|
||||
|
||||
```
|
||||
`sudo mount /dev/sda1`
|
||||
```
|
||||
|
||||
Finally, I linked MPD's configuration directory to the music with:
|
||||
|
||||
|
||||
```
|
||||
sudo rmdir /var/lib/mpd/music
|
||||
sudo ln -s /mnt/eSATA/Music /var/lib/mpd/music
|
||||
```
|
||||
|
||||
I used this approach because I'd rather not futz around with MPD's configuration more than I have to.
|
||||
|
||||
It's a good idea to check that the music files are owned by the correct MPD user ID. Do this with:
|
||||
|
||||
|
||||
```
|
||||
clh@stereo:~$ ls -ld /var/lib/mpd/music/* | head
|
||||
drwxrwxrwx 5 mpd audio 4096 Jan 22 2017 /var/lib/mpd/music/Academy of St. Martin-in-the-Fields
|
||||
drwxrwxrwx 3 mpd audio 4096 Jan 22 2017 /var/lib/mpd/music/Acantus
|
||||
drwxrwxrwx 3 mpd audio 4096 Dec 13 2014 /var/lib/mpd/music/Afrikan_Sciences
|
||||
drwxrwxrwx 5 mpd audio 4096 Sep 25 2016 /var/lib/mpd/music/Afro Celt Sound System
|
||||
drwxrwxrwx 3 mpd audio 4096 Mar 11 2014 /var/lib/mpd/music/Agnes Obel
|
||||
drwxrwxrwx 3 mpd audio 4096 Oct 9 2013 /var/lib/mpd/music/Alexander Malter, Dietmar Schwalke, Vladimir Spiakov, Sergej Bezrodny
|
||||
drwxrwxrwx 3 mpd audio 4096 Oct 8 2013 /var/lib/mpd/music/Ali Farka Toure & Toumani Diabate
|
||||
drwxrwxrwx 3 mpd audio 4096 Oct 8 2013 /var/lib/mpd/music/Ali Farka Touré with Ry Cooder
|
||||
drwxrwxrwx 5 mpd audio 4096 Jan 1 2018 /var/lib/mpd/music/Amadou et Mariam
|
||||
drwxrwxrwx 3 mpd audio 4096 Jul 11 2017 /var/lib/mpd/music/American Authors
|
||||
clh@stereo:~$
|
||||
```
|
||||
|
||||
This looks fine (user ID **mpd**, group ID **audio**).
|
||||
|
||||
Finally, to disable the wireless, I entered:
|
||||
|
||||
|
||||
```
|
||||
`sudo nmcli radio wifi off`
|
||||
```
|
||||
|
||||
And I'm done! Time for a reboot:
|
||||
|
||||
|
||||
```
|
||||
`sudo systemctl reboot`
|
||||
```
|
||||
|
||||
Once it rebooted, I used Cantata to force MPD to refresh its database. Here's what it looks like when that's done, and I'm listening to [Frameworks' latest album, _Imagine Gold_][25]:
|
||||
|
||||
![Frameworks "Imagine Gold" in Cantata][26]
|
||||
|
||||
Just to the left of the track listings, you can see the cover images that lighttpd serves.
|
||||
|
||||
### And speaking of music
|
||||
|
||||
I really do like Frameworks' latest album. It's quite similar to his previous work, and he always has the greatest hooks with very dance-able stuff and just that great bit of "yearning." It's nice to buy his stuff on Bandcamp, as usual (see link above, where you can also listen).
|
||||
|
||||
Lately, I've also been buying stuff in that other great open source format, vinyl records. In particular, two fine recent albums from the [Erased Tapes][27] label, [Anne Müller's _Heliopause_][28], which is a beautiful record of her own solo cello and supporting instruments and sounds, "focusing on novel approaches to classical instrumentation," and [Penguin Cafe's _Handfuls of Night_][29], which "began life after Greenpeace commissioned [Arthur] Jeffes [of Penguin Cafe] to write four pieces of music corresponding to four breeds of penguins to help raise awareness for the endangered Antarctic seas." How could a Linux user not have music about penguins by Penguin Cafe?
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/upgrade-cubox-open-source-music-server
|
||||
|
||||
作者:[Chris Hermansen][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/clhermansen
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/OSDC_LIFE_music_520x292_0411_ma.png?itok=v_ARy1ZT (open source music with piano keys background)
|
||||
[2]: https://en.wikipedia.org/wiki/ARM_architecture
|
||||
[3]: https://www.solid-run.com/nxp-family/cubox-i/
|
||||
[4]: https://en.wikipedia.org/wiki/Serial_ATA
|
||||
[5]: https://en.wikipedia.org/wiki/TOSLINK
|
||||
[6]: https://www.makeuseof.com/tag/5-mistakes-avoid-buying-next-microsd-card/
|
||||
[7]: https://www.nxp.com/products/processors-and-microcontrollers/arm-processors/i.mx-applications-processors/i.mx-6-processors/i.mx-6quad-processors-high-performance-3d-graphics-hd-video-arm-cortex-a9-core:i.MX6Q?&cid=ps_PRG100148_CAM262037_EETECH&gclid=CjwKCAiA27LvBRB0EiwAPc8XWfLpyKIpi4OPNrPLdmXpSynIaDP0FG2jQ9PPZ1q49ENp1CbQU2fKxxoCKtMQAvD_BwE
|
||||
[8]: https://www.musicpd.org/
|
||||
[9]: https://en.wikipedia.org/wiki/Advanced_Linux_Sound_Architecture
|
||||
[10]: https://www.lighttpd.net/
|
||||
[11]: https://opensource.com/article/17/8/cantata-music-linux
|
||||
[12]: https://play.google.com/store/apps/details?id=net.prezz.mpr&hl=en_CA
|
||||
[13]: https://opensource.com/article/17/6/armbian-cubox-i4pro
|
||||
[14]: https://wiki.debian.org/Derivatives/Census/VoyageLinux
|
||||
[15]: https://volumio.org/
|
||||
[16]: https://archphile.org/
|
||||
[17]: https://www.armbian.com/
|
||||
[18]: https://opensource.com/sites/default/files/uploads/armbianversions.png (Armbian versions)
|
||||
[19]: https://docs.armbian.com/
|
||||
[20]: https://www.etcher.io/
|
||||
[21]: https://opensource.com/sites/default/files/uploads/cuboxlogin.png (CuBox login)
|
||||
[22]: https://docs.armbian.com/User-Guide_Armbian-Config/
|
||||
[23]: https://opensource.com/sites/default/files/uploads/armbian-config.png (Armbian-config screen)
|
||||
[24]: https://opensource.com/sites/default/files/uploads/armbian-config_settings.png (Armbian-config settings)
|
||||
[25]: https://frameworksuk.bandcamp.com/album/imagine-gold
|
||||
[26]: https://opensource.com/sites/default/files/uploads/frameworks-imaginegold.png (Frameworks "Imagine Gold" in Cantata)
|
||||
[27]: https://www.erasedtapes.com/
|
||||
[28]: https://www.erasedtapes.com/release/eratp128-anne-mueller-heliopause
|
||||
[29]: https://www.erasedtapes.com/release/eratp127-penguin-cafe-handfuls-of-night
|
||||
@@ -1,380 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Use this script to create, save, and run different rsync configurations via named profiles)
|
||||
[#]: via: (https://opensource.com/article/20/1/create-save-run-rsync-configurations)
|
||||
[#]: author: (Petr Beránek https://opensource.com/users/pberanek)
|
||||
|
||||
Use this script to create, save, and run different rsync configurations via named profiles
|
||||
======
|
||||
A quick explanation of the rpf script.
|
||||
![Person drinking a hat drink at the computer][1]
|
||||
|
||||
The **rpf** script allows you to create, save, and run different rsync configurations via named profiles.
|
||||
|
||||
For example, create a new profile named **backup** by typing **rpf -c backup**. Assume that the username is **user**.
|
||||
|
||||
**rpf** creates the following directories:
|
||||
|
||||
* **/home/user/.rpf**
|
||||
* **/home/user/.rpf/shared** where you can place config files shared by multiple profiles
|
||||
* **/home/user/.rpf/profiles** where all profiles are saved as subdirectories
|
||||
|
||||
|
||||
|
||||
**rpf** also created **/home/user/.rpf/profiles/backup** that contains the files **conf** and **excluded**.
|
||||
|
||||
The **conf** file defines rsync's configuration:
|
||||
|
||||
|
||||
```
|
||||
# rsync config template
|
||||
#
|
||||
# Write each rsync option on separate line. For option details see man rsync.
|
||||
# Empty lines and lines starting with # are ignored. Dynamic references
|
||||
# (e.g. using command substitution) are not supported.
|
||||
#
|
||||
# Config files shared between different profiles should be saved in
|
||||
# /home/user/.rpf/shared
|
||||
#
|
||||
# Example configuration:
|
||||
#
|
||||
\--verbose
|
||||
\--archive
|
||||
\--human-readable
|
||||
# exclude all files that match pattern in:
|
||||
\--exclude-from=/home/user/.rpf/profiles/backup/exclude
|
||||
\--relative
|
||||
# perform trial run, make no changes
|
||||
\--dry-run
|
||||
# source, e.g.
|
||||
/home/user
|
||||
# destination, e.g.
|
||||
/mnt/usb_drive/users_backup
|
||||
```
|
||||
|
||||
Now you can edit, add, or remove rsync options as needed.
|
||||
|
||||
In **exclude**, you can define paths or patterns of files and directories you want to exclude from the transfer. To exclude **Trash** and **Downloads**, add the following lines:
|
||||
|
||||
|
||||
```
|
||||
\- /home/user/.local/share/Trash
|
||||
\- /home/user/Downloads
|
||||
```
|
||||
|
||||
Or to transfer only **Documents** and **Projects** and exclude everything else:
|
||||
|
||||
|
||||
```
|
||||
\+ /home/user/Documents
|
||||
\+ /home/user/Projects
|
||||
\- **
|
||||
```
|
||||
|
||||
For subtler pattern configurations, see the FILTER RULES section of **man rsync**, or Google for tutorials.
|
||||
|
||||
When you are ready, you can start rsync transfer by typing **rpf backup**.
|
||||
|
||||
That's it.
|
||||
|
||||
For additional **rpf** options, see **rpf --help**.
|
||||
|
||||
### Security
|
||||
|
||||
Please be aware that **rpf** is not secure against code injection in the **conf** file. Any additional code (e.g., **; ./run_evil_script**) will also be executed. Therefore, protect your **.rpf/** config directory from malicious users by implementing appropriate permissions. Moreover, exploiting this behavior can lead to unexpected side effects.
|
||||
|
||||
### The script
|
||||
|
||||
|
||||
```
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Simple rsync profiler
|
||||
#
|
||||
# Author: [petrberanek.mail@gmail.com][2] (Petr Beranek)
|
||||
#
|
||||
# For usage details type `rpf --help'
|
||||
#
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
__name=$(basename "${0}")
|
||||
__version="0.1"
|
||||
|
||||
config_dir="${HOME}/.rpf"
|
||||
profiles_dir="${config_dir}/profiles"
|
||||
shared_dir="${config_dir}/shared"
|
||||
help="\
|
||||
Usage: ${__name} [OPTION...] PROFILE_NAME
|
||||
|
||||
${__name} is simple rsync profiler that stores your different rsync
|
||||
configurations in named profiles.
|
||||
|
||||
Options:
|
||||
-c, --create-profile PROFILE_NAME create new profile (profile data
|
||||
are stored in ${config_dir}/PROFILE_NAME).
|
||||
Profile name can contain alphanumeric
|
||||
characters only.
|
||||
-s, --show-profile-config PROFILE_NAME show content of profile
|
||||
configuration file (stored in
|
||||
${config_dir}/PROFILE_NAME)
|
||||
-l, --list-profiles list all available profiles
|
||||
-h, --help show this help
|
||||
|
||||
Example:
|
||||
Create new profile by typing
|
||||
${__name} -c PROFILE_NAME
|
||||
|
||||
edit its config files stored by default in
|
||||
${profiles_dir}/PROFILE_NAME
|
||||
|
||||
and then run it by typing
|
||||
${__name} PROFILE_NAME
|
||||
|
||||
That's it.
|
||||
|
||||
${__name} comes with ABSOLUTELY NO WARRANTY. This is free software,
|
||||
and you are welcome to redistribute it under certain conditions. See
|
||||
the GNU General Public Licence for details.
|
||||
|
||||
Email bug reports or enhancement requests to [petrberanek.mail@gmail.com][2].
|
||||
"
|
||||
|
||||
create_profile() {
|
||||
# Create dir with given profile name and with default content.
|
||||
#
|
||||
# Arguments: $1 -- profile name
|
||||
#
|
||||
# Creates files: conf, exclude
|
||||
#
|
||||
# If dir with the same name already exists, exits with error.
|
||||
#
|
||||
|
||||
local profile_name="${1}"
|
||||
local profile_dir="${profiles_dir}/${profile_name}"
|
||||
|
||||
# create default rpf dirs if missing
|
||||
if [[ ! -d "${profiles_dir}" ]]; then
|
||||
echo "Creating ${profiles_dir}"
|
||||
mkdir --parents "${profiles_dir}"
|
||||
fi
|
||||
if [[ ! -d "${shared_dir}" ]]; then
|
||||
echo "Creating ${shared_dir}"
|
||||
mkdir --parents "${shared_dir}"
|
||||
fi
|
||||
|
||||
# don't overwrite existing profile
|
||||
if [[ -d "${profile_dir}" ]]; then
|
||||
echo "${__name}: error: profile already exists."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Creating ${profile_dir}"
|
||||
mkdir "${profile_dir}"
|
||||
|
||||
# create `conf' template
|
||||
local conf="${profile_dir}/conf"
|
||||
echo "Creating ${conf}"
|
||||
cat << EOF > "${conf}"
|
||||
# rsync config template
|
||||
#
|
||||
# Write each rsync option on separate line. For details see man rsync.
|
||||
# Empty lines and lines starting with # are ignored. Dynamic references
|
||||
# (e.g. using command substitution) are not supported.
|
||||
#
|
||||
# Config files shared between different profiles should be saved in
|
||||
# ${shared_dir}
|
||||
#
|
||||
# Example configuration:
|
||||
#
|
||||
\--verbose
|
||||
\--archive
|
||||
\--human-readable
|
||||
# file with patterns of files and directories in source excluded
|
||||
# from transfer
|
||||
\--exclude-from="${profiles_dir}/${profile_name}/exclude"
|
||||
\--relative
|
||||
# perform trial run, make no changes
|
||||
\--dry-run
|
||||
# source, e.g.
|
||||
${HOME}
|
||||
# destination, e.g.
|
||||
/mnt/usb_drive/my_backup
|
||||
EOF
|
||||
|
||||
# create `exclude' template
|
||||
local exclude="${profile_dir}/exclude"
|
||||
echo "Creating ${exclude}"
|
||||
cat << EOF > "${exclude}"
|
||||
# \\`exclude' template
|
||||
#
|
||||
# Lines starting with # or ; are ignored. For details see man rsync,
|
||||
# section FILTER RULES.
|
||||
#
|
||||
EOF
|
||||
|
||||
# all done
|
||||
echo "OK"
|
||||
echo "Edit profile config files in ${profile_dir} to fit your needs."
|
||||
}
|
||||
|
||||
list_profiles() {
|
||||
# Show all available rpf profiles.
|
||||
#
|
||||
# Assumes that all dirs in $profiles_dir are profiles.
|
||||
#
|
||||
|
||||
for item in "${profiles_dir}"/*; do
|
||||
if [[ -d "${item}" ]]; then
|
||||
basename "${item}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
show_help() { echo "${help}"; }
|
||||
|
||||
show_profile_config() {
|
||||
# Show configuration file for given profile.
|
||||
#
|
||||
# Arguments: $1 -- profile name
|
||||
#
|
||||
|
||||
local profile_name="${1}"
|
||||
less "${profiles_dir}/${profile_name}/conf"
|
||||
}
|
||||
|
||||
check_profile_name() {
|
||||
# Check that name is not empty and contains alphanumeric chars only.
|
||||
#
|
||||
# Arguments: $1 -- profile name
|
||||
#
|
||||
# If test fails, exits with error.
|
||||
#
|
||||
|
||||
if [[ -z "${1}" ]]; then
|
||||
echo "${__name}: error: empty profile name."
|
||||
exit 1
|
||||
elif [[ "${1}" =~ [^a-zA-Z0-9] ]]; then
|
||||
echo "${__name}: error: non-alphanumeric characters in profile name."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_profile_exists() {
|
||||
# Check that $profile_name exists and is a directory.
|
||||
#
|
||||
# Arguments: $1 -- profile name
|
||||
#
|
||||
# If test fails, exits with error.
|
||||
#
|
||||
|
||||
local profile_name="${1}"
|
||||
if [[ ! -d "${profiles_dir}/${profile_name}" ]]; then
|
||||
echo "${__name}: error: profile ${profile_name} does not exist."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_num_args() {
|
||||
# Check that value of $1 = number of arguments (excluding $1)
|
||||
#
|
||||
# Arguments: $1 -- limit (positive int)
|
||||
#
|
||||
# If test fails, exits with error.
|
||||
#
|
||||
|
||||
local num_args=$(( ${#} - 1 )) # do not count $1 in total num of args
|
||||
if [[ "${1}" -ne "${num_args}" ]]; then
|
||||
echo "${__name}: error: expected num args: ${1}, received: $num_args"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
run_rsync() {
|
||||
# Run rsync with configuration coresponding to given profile name.
|
||||
#
|
||||
# Arguments: $1 -- profile name
|
||||
#
|
||||
|
||||
local profile_name="${1}"
|
||||
local visual_div="=============================="
|
||||
local parsed_args
|
||||
parsed_args=$(grep --invert-match '^#' "${profiles_dir}/${profile_name}/conf" \
|
||||
| tr '\n' ' ')
|
||||
|
||||
# Print debug info
|
||||
echo "${visual_div}"
|
||||
echo "${__name} version: ${__version}"
|
||||
echo "args: ${parsed_args}"
|
||||
echo "${visual_div}"
|
||||
|
||||
# Expand $parsed_args - each item from conf file becomes rsync argument
|
||||
# shellcheck disable=SC2086
|
||||
rsync ${parsed_args}
|
||||
}
|
||||
|
||||
if [[ "${#}" == 0 ]]; then
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
while [[ "${#}" -gt 0 ]]; do
|
||||
case "${1}" in
|
||||
-c | --create-profile)
|
||||
check_num_args 2 "${@}"
|
||||
shift
|
||||
check_profile_name "${1:-}" # If $1 is not declared, set it empty.
|
||||
create_profile "${1}"
|
||||
exit 0;;
|
||||
-s | --show-profile-config)
|
||||
check_num_args 2 "${@}"
|
||||
shift
|
||||
check_profile_name "${1:-}"
|
||||
check_profile_exists "${1}"
|
||||
show_profile_config "${1}"
|
||||
exit 0;;
|
||||
-l | --list-profiles)
|
||||
check_num_args 1 "${@}"
|
||||
list_profiles
|
||||
exit 0;;
|
||||
-h | --help)
|
||||
check_num_args 1 "${@}"
|
||||
show_help
|
||||
exit 0;;
|
||||
-*)
|
||||
echo "${__name}: error: unknown option \\`${1}'"
|
||||
exit 1;;
|
||||
*)
|
||||
check_num_args 1 "${@}"
|
||||
check_profile_name "${1:-}"
|
||||
check_profile_exists "${1}"
|
||||
run_rsync "${1}"
|
||||
exit 0;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
```
|
||||
|
||||
* * *
|
||||
|
||||
This was originally published in [Petr Beranek's GitHub repository][3] and it is licensed under GPLv3.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/create-save-run-rsync-configurations
|
||||
|
||||
作者:[Petr Beránek][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/pberanek
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/coffee_tea_laptop_computer_work_desk.png?itok=D5yMx_Dr (Person drinking a hat drink at the computer)
|
||||
[2]: mailto:petrberanek.mail@gmail.com
|
||||
[3]: https://github.com/beranep/rpf
|
||||
@@ -1,127 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (LMMS: A Free & Open Source Digital Audio Workstation (DAW))
|
||||
[#]: via: (https://itsfoss.com/lmms/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
LMMS: A Free & Open Source Digital Audio Workstation (DAW)
|
||||
======
|
||||
|
||||
_**In this week’s Linux application highlight, we take a look at a free and open-source DAW that helps make music with most of the essential features offered.**_
|
||||
|
||||
### LMMS: A Free & Open Source Software To Help Make Music
|
||||
|
||||
[LMMS][1] is a cross-platform open source [DAW][2] hosted on [GitHub][3]. It is completely free to use and you do not need to purchase any kind of license to use it.
|
||||
|
||||
If you’re curious, there’s no specific full-form for “LMMS” acronym but you can consider it along the lines of “**Let’s Make Music**” or formerly known as “**Linux MultiMedia Studio**” as stated in one of their [official forum post][4] years back.
|
||||
|
||||
So, with the help of LMMS, you should be able to work on making music on Linux.
|
||||
|
||||
Of course, you should not expect a free DAW to replace a full-fledged professional DAW bundled with proprietary plugins – but for starters, it isn’t a bad one.
|
||||
|
||||
![][5]
|
||||
|
||||
If you’re used to other DAWs, it might take a while to get comfortable with the user interface. For instance, I’ve used [Studio One][6] and [Mixcraft][7] as a beginner – so the UI of LMMS looked different.
|
||||
|
||||
But, the good news is – you get a [detailed official documentation][8] of LMMS. So, if you have trouble learning how it works, simply refer to the official documentation available on their [website][1].
|
||||
|
||||
**Recommended Read:**
|
||||
|
||||
![][9]
|
||||
|
||||
#### [Best Audio Editors For Linux][10]
|
||||
|
||||
These awesome free and open source audio editors let you create awesome music in Linux. Check out the list of top Linux audio editors.
|
||||
|
||||
### Features of LMMS
|
||||
|
||||
![][11]
|
||||
|
||||
Just like any other DAW, you will find plugins, samples to use, instruments, and MIDI support. However, it is indeed a long list of features. So, instead of taking a whole day explaining it, let me highlight all the necessary features that LMMS provides.
|
||||
|
||||
* Ability to add notes within the project without needing a separate note-taking app
|
||||
* Bundled free plugins to use
|
||||
* Note playback via MIDI or typing keyboard
|
||||
* MIDI Editor
|
||||
* Separate editor for instruments
|
||||
* Track Automation support
|
||||
* 64-bit VST instrument support (for wine-based VSTs)
|
||||
* Built-in synthesizers that include some popular emulators for Yamaha and Roland
|
||||
* Feature-rich audio plugins built-in
|
||||
* Demo projects to easily get started
|
||||
* Several samples included
|
||||
* Native multisample support for SoundFont (SF2), Giga (GIG) and Gravis UltraSound (GUS) formats for high-quality instrument patches and banks (only if it’s relevant for your work)
|
||||
* [LADSPA][12] plugin support
|
||||
* You will also find the essential delay/reverb/compressor/limiter and distortion tools built-in
|
||||
* Spectrum analyzer
|
||||
|
||||
|
||||
|
||||
You can find some extensive [documentation on using LMMS][8]. If you face any difficulties, LMMS also has its [own forum][13] where you can ask for help.
|
||||
|
||||
### Installing LMMS on Linux
|
||||
|
||||
![][14]
|
||||
|
||||
You can also find LMMS listed in the software center of your distribution- however, it might not feature the latest version.
|
||||
|
||||
To get the latest LMMS version, you can download the .**AppImage** file from the [official download page][15]. If you want to take a look at the latest or previous releases on GitHub, you can find those in their [releases][16] section.
|
||||
|
||||
If you want to use the AppImage file, you can refer to our guide on [using AppImage file][17].
|
||||
|
||||
You may also try [installing the Flatpak package][18] available on [Flathub][19].
|
||||
|
||||
[Download LMMS][15]
|
||||
|
||||
### My Thoughts On LMMS
|
||||
|
||||
![][20]
|
||||
|
||||
Even though this is a feature-rich free and open-source DAW – personally, I found the UX a little dull.
|
||||
|
||||
Of course, technically, it offers quite a lot of features and this could be useful for anyone who does not want to break their wallet to produce music. And, after all, it is an open-source solution.
|
||||
|
||||
If you want a full-fledged professional DAW using the VST plugins, you might just hold on to Windows/Mac (if you’re used to it) because you still need to utilize Wine on Linux in order to make sure that the 64-bit VSTs work.
|
||||
|
||||
**Wrapping Up**
|
||||
|
||||
That being said, I’m not a professional music producer – so feel free to explore LMMS on Linux and make the most out of it without spending a penny.
|
||||
|
||||
If you’ve used LMMS (or any similar DAW), let me know your thoughts in the comments below.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/lmms/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://lmms.io/
|
||||
[2]: https://en.wikipedia.org/wiki/Digital_audio_workstation
|
||||
[3]: https://github.com/LMMS/lmms
|
||||
[4]: https://lmms.io/forum/viewtopic.php?t=5908
|
||||
[5]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/01/lmms-screenshot.png?ssl=1
|
||||
[6]: https://www.presonus.com/products/studio-one/
|
||||
[7]: https://acoustica.com/mixcraft
|
||||
[8]: https://docs.lmms.io/user-manual/
|
||||
[9]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/01/linux-audio-editors.jpeg?fit=800%2C450&ssl=1
|
||||
[10]: https://itsfoss.com/best-audio-editors-linux/
|
||||
[11]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/01/lmms-project.png?ssl=1
|
||||
[12]: https://www.ladspa.org/
|
||||
[13]: https://lmms.io/forum/
|
||||
[14]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/01/lmms-setup.jpg?ssl=1
|
||||
[15]: https://lmms.io/download#linux
|
||||
[16]: https://github.com/LMMS/lmms/releases
|
||||
[17]: https://itsfoss.com/use-appimage-linux/
|
||||
[18]: https://itsfoss.com/flatpak-guide/
|
||||
[19]: https://flathub.org/apps/details/io.lmms.LMMS
|
||||
[20]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/01/lmms-midi-editor.png?ssl=1
|
||||
@@ -1,385 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Automating Helm deployments with Bash)
|
||||
[#]: via: (https://opensource.com/article/20/1/automating-helm-deployments-bash)
|
||||
[#]: author: (Darin London https://opensource.com/users/dmlond)
|
||||
|
||||
Automating Helm deployments with Bash
|
||||
======
|
||||
This Bash script enables developers to focus on their code rather than
|
||||
the deployment logic in each project.
|
||||
![Person using a laptop][1]
|
||||
|
||||
Some of our applications are hosted in a [Kubernetes][2] cluster, and we use GitLab Continuous Integration (CI) to automate deployments and Helm 2 to deploy our applications. Helm charts enable the storage of templates of Kubernetes object YAML files with variables that can be programmatically set from command-line arguments passed when the chart is used during deployments. This allows us to store critical secrets in GitLab-protected environment variables or in Hashicorp Vault and use them within the CI deployment job.
|
||||
|
||||
Our deployment job uses a [Bash script to run the deployment process][3]. This Bash script presents a number of features that are valuable for use within a CI/CD environment:
|
||||
|
||||
1. It facilitates use outside of the CI/CD environment. GitLab CI and other CI systems store job steps as lines of executable shell code in a "script" section of a CI text file (.gitlab-ci.yml, for example). While this is useful to ensure basic executable steps can be stored without external dependencies, it prevents developers from using the same code in testing or manual deployment scenarios. In addition, many advanced features of the Bash system cannot be easily used in these script sections.
|
||||
2. It facilitates unit testing of important deployment processes. None of the CI systems provide a way of testing whether deployment logic performs as expected. Carefully constructed [Bash scripts can be unit tested with BATS][4].
|
||||
3. It facilitates reuse of individual functions within the script. The last section uses a guard clause, **if [[ "${BASH_SOURCE[0]}" == "${0}" ]]**, which prevents the **run_main** function from being called when the script is not being executed. This allows the script to be sourced, which then allows users to make use of the many useful individual functions within it. This is crucial for proper BATS testing.
|
||||
4. It uses environment variables to protect sensitive information and make the script reusable across many projects and project application environments. GitLab CI makes many of these environment variables available when run by a GitLab CI runner. These must be manually set before using the script outside GitLab CI.
|
||||
|
||||
|
||||
|
||||
The script performs all tasks required to deploy a Helm chart for an application to Kubernetes and waits for the deployment to be ready using kubectl and Helm. Helm runs with a local Tiller installation instead of running Tiller in the Kubernetes cluster. The Kubernetes **HELM_USER** and **HELM_PASSWORD** are used to log into the Kubernetes **CLUSTER_SERVER** and **PROJECT_NAMESPACE**. Tiller is started, Helm is initialized in client-only mode, and its repo is updated. The template is linted with Helm to ensure that syntax errors have not been accidentally committed. The template is then deployed in declarative mode, using **helm upgrade --install**. Helm waits for the deployment to be ready using the **\--wait flag**.
|
||||
|
||||
The script ensures that certain template variables are set during the deployment and allows special project-specific variables to be specified in the GitLab CI **PROJECT_SPECIFIC_DEPLOY_ARGS** environment variable. All environment variables required in the deployment are checked early in the script execution, and the script exits with a non-zero exit status if any are missing.
|
||||
|
||||
This script has been used in multiple GitLab CI-hosted projects. It has helped us focus on our code rather than the deployment logic in each project.
|
||||
|
||||
### The script
|
||||
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2019 Darin London
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
log_level_for()
|
||||
{
|
||||
case "${1}" in
|
||||
"error")
|
||||
echo 1
|
||||
;;
|
||||
|
||||
"warn")
|
||||
echo 2
|
||||
;;
|
||||
|
||||
"debug")
|
||||
echo 3
|
||||
;;
|
||||
|
||||
"info")
|
||||
echo 4
|
||||
;;
|
||||
*)
|
||||
echo -1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
current_log_level()
|
||||
{
|
||||
log_level_for "${LOG_LEVEL}"
|
||||
}
|
||||
|
||||
error()
|
||||
{
|
||||
[ $(log_level_for "error") -le $(current_log_level) ] && echo "${1}" >&2
|
||||
}
|
||||
|
||||
warn()
|
||||
{
|
||||
[ $(log_level_for "warn") -le $(current_log_level) ] && echo "${1}" >&2
|
||||
}
|
||||
|
||||
debug()
|
||||
{
|
||||
[ $(log_level_for "debug") -le $(current_log_level) ] && echo "${1}" >&2
|
||||
}
|
||||
|
||||
info()
|
||||
{
|
||||
[ $(log_level_for "info") -le $(current_log_level) ] && echo "${1}" >&2
|
||||
}
|
||||
|
||||
check_required_environment() {
|
||||
local required_env="${1}"
|
||||
|
||||
for reqvar in $required_env
|
||||
do
|
||||
if [ -z "${!reqvar}" ]
|
||||
then
|
||||
error "missing ENVIRONMENT ${reqvar}!"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
check_default_environment() {
|
||||
local required_env="${1}"
|
||||
|
||||
for varpair in $required_env
|
||||
do
|
||||
local manual_environment=$(echo "${varpair}" | cut -d':' -f1)
|
||||
local default_if_not_set=$(echo "${varpair}" | cut -d':' -f2)
|
||||
if [ -z "${!manual_environment}" ] && [ -z "${!default_if_not_set}" ]
|
||||
then
|
||||
error "missing default ENVIRONMENT, set ${manual_environment} or ${default_if_not_set}!"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
dry_run() {
|
||||
[ ${DRY_RUN} ] && info "skipping for dry run" && return
|
||||
return 1
|
||||
}
|
||||
|
||||
init_tiller() {
|
||||
info "initializing local tiller"
|
||||
dry_run && return
|
||||
|
||||
export TILLER_NAMESPACE=$PROJECT_NAMESPACE
|
||||
export HELM_HOST=localhost:44134
|
||||
# <https://rimusz.net/tillerless-helm/>
|
||||
# run tiller locally instead of in the cluster
|
||||
tiller --storage=secret &
|
||||
export TILLER_PID=$!
|
||||
sleep 1
|
||||
kill -0 ${TILLER_PID}
|
||||
if [ $? -gt 0 ]
|
||||
then
|
||||
error "tiller not running!"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
init_helm() {
|
||||
info "initializing helm"
|
||||
dry_run && return
|
||||
|
||||
helm init --client-only
|
||||
if [ $? -gt 0 ]
|
||||
then
|
||||
error "could not initialize helm"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
init_helm_with_tiller() {
|
||||
init_tiller || return 1
|
||||
init_helm || return 1
|
||||
info "updating helm client repository information"
|
||||
dry_run && return
|
||||
helm repo update
|
||||
if [ $? -gt 0 ]
|
||||
then
|
||||
error "could not update helm repository information"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
decommission_tiller() {
|
||||
if [ -n "${TILLER_PID}" ]
|
||||
then
|
||||
kill ${TILLER_PID}
|
||||
if [ $? -gt 0 ]
|
||||
then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_required_deploy_arg_environment() {
|
||||
[ -z "${PROJECT_SPECIFIC_DEPLOY_ARGS}" ] && return
|
||||
for reqvar in ${PROJECT_SPECIFIC_DEPLOY_ARGS}
|
||||
do
|
||||
if [ -z ${!reqvar} ]
|
||||
then
|
||||
error "missing Deployment ENVIRONMENT ${reqvar} required!"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
project_specific_deploy_args() {
|
||||
[ -z "${PROJECT_SPECIFIC_DEPLOY_ARGS}" ] && echo "" && return
|
||||
|
||||
extraArgs=''
|
||||
for deploy_arg_key in ${PROJECT_SPECIFIC_DEPLOY_ARGS}
|
||||
do
|
||||
extraArgs="${extraArgs} --set $(echo "${deploy_arg_key}" | sed 's/__/\\./g' | tr '[:upper:]' '[:lower:]')=${!deploy_arg_key}"
|
||||
done
|
||||
|
||||
echo "${extraArgs}"
|
||||
}
|
||||
|
||||
check_required_cluster_login_environment() {
|
||||
check_required_environment "HELM_TOKEN HELM_USER PROJECT_NAMESPACE CLUSTER_SERVER" || return 1
|
||||
}
|
||||
|
||||
cluster_login() {
|
||||
info "authenticating ${HELM_USER} in ${PROJECT_NAMESPACE}"
|
||||
dry_run && return
|
||||
|
||||
kubectl config set-cluster ci_kube --server="${CLUSTER_SERVER}" || return 1
|
||||
kubectl config set-credentials "${HELM_USER}" --token="${HELM_TOKEN}" || return 1
|
||||
kubectl config set-context ${PROJECT_NAMESPACE}-deploy --cluster=ci_kube --namespace=${PROJECT_NAMESPACE} --user=${HELM_USER} || return 1
|
||||
kubectl config use-context ${PROJECT_NAMESPACE}-deploy || return 1
|
||||
}
|
||||
|
||||
lint_template() {
|
||||
info "linting template"
|
||||
dry_run && return
|
||||
|
||||
helm lint ${CI_PROJECT_DIR}/helm-chart/${CI_PROJECT_NAME}
|
||||
}
|
||||
|
||||
check_required_image_pull_environment() {
|
||||
if [ "${CI_PROJECT_VISIBILITY}" == "public" ]
|
||||
then
|
||||
check_required_environment "CI_REGISTRY CI_DEPLOY_USER CI_DEPLOY_PASSWORD" || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
image_pull_settings() {
|
||||
if [ "${CI_PROJECT_VISIBILITY}" == "public" ]
|
||||
then
|
||||
echo ""
|
||||
else
|
||||
echo "--set registry.root=${CI_REGISTRY} --set registry.secret.username=${CI_DEPLOY_USER} --set registry.secret.password=${CI_DEPLOY_PASSWORD}"
|
||||
fi
|
||||
}
|
||||
|
||||
deployment_name() {
|
||||
if [ -n "${DEPLOYMENT_NAME}" ]
|
||||
then
|
||||
echo "${DEPLOYMENT_NAME}"
|
||||
else
|
||||
echo "${CI_ENVIRONMENT_SLUG}-${CI_PROJECT_NAME}"
|
||||
fi
|
||||
}
|
||||
|
||||
deploy_template() {
|
||||
info "deploying $(deployment_name) from template"
|
||||
if dry_run
|
||||
then
|
||||
info "helm upgrade --force --recreate-pods --debug --set image.repository=${CI_REGISTRY_IMAGE}/${CI_PROJECT_NAME} --set image.tag=${CI_COMMIT_SHORT_SHA} --set environment=${CI_ENVIRONMENT_NAME} --set-string git_commit=${CI_COMMIT_SHORT_SHA} --set git_ref=${CI_COMMIT_REF_SLUG} --set ci_job_id=${CI_JOB_ID} $(environment_url_settings) $(image_pull_settings) $(project_specific_deploy_args) --wait --install $(deployment_name) ${CI_PROJECT_DIR}/helm-chart/${CI_PROJECT_NAME}"
|
||||
else
|
||||
helm upgrade --force --recreate-pods --debug \
|
||||
--set image.repository="${CI_REGISTRY_IMAGE}/${CI_PROJECT_NAME}" \
|
||||
--set image.tag="${CI_COMMIT_SHORT_SHA}" \
|
||||
--set environment="${CI_ENVIRONMENT_NAME}" \
|
||||
--set-string git_commit="${CI_COMMIT_SHORT_SHA}" \
|
||||
--set git_ref="${CI_COMMIT_REF_SLUG}" \
|
||||
--set ci_job_id="${CI_JOB_ID}" \
|
||||
$(image_pull_settings) \
|
||||
$(project_specific_deploy_args) \
|
||||
--wait \
|
||||
--install $(deployment_name) ${CI_PROJECT_DIR}/helm-chart/${CI_PROJECT_NAME}
|
||||
fi
|
||||
}
|
||||
|
||||
get_pods() {
|
||||
kubectl get pods -l ci_job_id="${CI_JOB_ID}"
|
||||
}
|
||||
|
||||
watch_deployment() {
|
||||
local watch_deployment=$(deployment_name)
|
||||
if [ -n "${WATCH_DEPLOYMENT}" ]
|
||||
then
|
||||
watch_deployment="${WATCH_DEPLOYMENT}"
|
||||
fi
|
||||
info "waiting until deployment ${watch_deployment} is ready"
|
||||
dry_run && return
|
||||
|
||||
kubectl rollout status deployment/${watch_deployment} -w || return 1
|
||||
sleep 5
|
||||
get_pods || return 1
|
||||
# see what has been deployed
|
||||
kubectl describe deployment -l app=${CI_PROJECT_NAME},environment=${CI_ENVIRONMENT_NAME},git_commit=${CI_COMMIT_SHORT_SHA} || return 1
|
||||
if [ -n "${CI_ENVIRONMENT_URL}" ]
|
||||
then
|
||||
kubectl describe service -l app=${CI_PROJECT_NAME},environment=${CI_ENVIRONMENT_NAME} || return 1
|
||||
kubectl describe route -l app=${CI_PROJECT_NAME},environment=${CI_ENVIRONMENT_NAME} || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
run_main() {
|
||||
check_required_environment "CI_PROJECT_NAME CI_PROJECT_DIR CI_COMMIT_REF_SLUG CI_REGISTRY_IMAGE CI_ENVIRONMENT_NAME CI_JOB_ID CI_COMMIT_SHORT_SHA" || return 1
|
||||
check_default_environment "WATCH_DEPLOYMENT:CI_ENVIRONMENT_SLUG" || return 1
|
||||
check_required_deploy_arg_environment || return 1
|
||||
check_required_cluster_login_environment || return 1
|
||||
check_required_image_pull_environment || return 1
|
||||
cluster_login
|
||||
if [ $? -gt 0 ]
|
||||
then
|
||||
error "could not login kubectl"
|
||||
return 1
|
||||
fi
|
||||
|
||||
init_helm_with_tiller
|
||||
if [ $? -gt 0 ]
|
||||
then
|
||||
error "could not initialize helm"
|
||||
return 1
|
||||
fi
|
||||
|
||||
lint_template
|
||||
if [ $? -gt 0 ]
|
||||
then
|
||||
error "linting failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
deploy_template
|
||||
if [ $? -gt 0 ]
|
||||
then
|
||||
error "could not deploy template"
|
||||
return 1
|
||||
fi
|
||||
|
||||
watch_deployment
|
||||
if [ $? -gt 0 ]
|
||||
then
|
||||
error "could not watch deployment"
|
||||
return 1
|
||||
fi
|
||||
|
||||
decommission_tiller
|
||||
info "ALL Complete!"
|
||||
return
|
||||
}
|
||||
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]
|
||||
then
|
||||
run_main
|
||||
if [ $? -gt 0 ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/automating-helm-deployments-bash
|
||||
|
||||
作者:[Darin London][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/dmlond
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/laptop_screen_desk_work_chat_text.png?itok=UXqIDRDD (Person using a laptop)
|
||||
[2]: https://opensource.com/resources/what-is-kubernetes
|
||||
[3]: https://gist.github.com/dmlond/016ff7c3757fee00f9d02e36e6c0c298
|
||||
[4]: https://opensource.com/article/19/2/testing-bash-bats
|
||||
@@ -1,108 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Zorin Grid Lets You Remotely Manage Multiple Zorin OS Computers)
|
||||
[#]: via: (https://itsfoss.com/zorin-grid/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
Zorin Grid Lets You Remotely Manage Multiple Zorin OS Computers
|
||||
======
|
||||
|
||||
One of the major hurdles institutes face is in managing and updating multiple Linux systems from a central point.
|
||||
|
||||
Well, Zorin OS has come up with a new cloud-based tool that will help you manage multiple computers running Zorin OS from one single interface. You can update the systems, install apps and configuration all systems remotely using this tool called [Zorin Grid][1].
|
||||
|
||||
### Zorin Grid: Manage a fleet of Zorin OS computers remotely
|
||||
|
||||
![][2]
|
||||
|
||||
**Zorin Grid is a tool that makes it simple to set up, manage, and secure a fleet of Zorin OS-powered computers in businesses, schools, and organizations.**
|
||||
|
||||
When it comes to managing Linux distributions (here, Zorin OS) on a multitude of systems for an organization – it is quite time-consuming.
|
||||
|
||||
If it will be easier to manage Linux systems, more organizations will be interested to switch using Linux just like the [Italian city Vicenza replaced Windows by Zorin OS][3].
|
||||
|
||||
For the very same reason, the Zorin team decided to create ‘**Zorin Grid**‘ with the help of which every school, enterprises, organizations, and businesses will be able to easily manage their Zorin OS-powered machines.
|
||||
|
||||
### Zorin Grid features
|
||||
|
||||
![Zorin Grid Features][4]
|
||||
|
||||
You might have guessed what it is capable of – but let me highlight the key features of Zorin Grid as per its official webpage:
|
||||
|
||||
* Install and Remove Apps
|
||||
* Set software update and security patch policies
|
||||
* Monitor computer status
|
||||
* Enforce security policies
|
||||
* Keep track of software and hardware inventory
|
||||
* Set desktop settings
|
||||
* Organize computers into groups (for teams and departments)
|
||||
* Role-based access control and audit logging
|
||||
|
||||
|
||||
|
||||
In addition to these, you will be able to do a couple more things using the Zorin Grid service. But, it looks like most of the essential tasks will be covered by Zorin Grid.
|
||||
|
||||
### How does Zorin Grid work?
|
||||
|
||||
![][5]
|
||||
|
||||
Zorin Grid is a cloud based software as a service. Zorin will be charging a monthly subscription fee for each computer managed by Zorin Grid in an organization.
|
||||
|
||||
You’ll have to install the Zorin Grid client on all the systems that you want to manage. Since it is cloud-based, you can manage all the Zorin systems on your grid from a web browser by logging into you Zorin Grid account.
|
||||
|
||||
You choose how to configure the computers once and the Zorin Grid applies the same configuration to all or specific computers in your organization.
|
||||
|
||||
The price has not been finalized. [Artyom Zorin][6], **CEO of Zorin Group, told It’s FOSS** that schools and non-profit organizations will get Zorin Grid for a reduced pricing.
|
||||
|
||||
While client-side software for Zorin Grid will be open source, the Zorin Grid server won’t be open source initially. Releasing it under an open source license is _tentatively_ on their roadmap.
|
||||
|
||||
Artyom also told that they **plan to support other Linux distributions starting with Ubuntu and Ubuntu-based distros** after launching Zorin Grid for Zorin OS systems this summer.
|
||||
|
||||
In case you decide to migrate from Windows to Zorin OS for your organization or business, you will find a [useful migration guide][7] by the Zorin OS team to help you switch to Linux.
|
||||
|
||||
[Zorin Grid][1]
|
||||
|
||||
**Wrapping Up**
|
||||
|
||||
Let me summarize all the important points about Zorin Grid:
|
||||
|
||||
* Zorin Grid is an upcoming cloud based service that lets you manage multiple Zorin OS systems.
|
||||
* It’s a premium service that charges for each computer used. The pricing is not determined yet.
|
||||
* Educational institutes and non-profit organizations can get Zorin Grid for a reduced pricing.
|
||||
* Initially it can only handle Zorin OS. Other Ubuntu-based distributions are on the road-map but there is no definite timeline for that.
|
||||
* The service should be available in the summer 2020.
|
||||
* Zorin Grid server won’t be open source initially.
|
||||
|
||||
|
||||
|
||||
Zorin Grid looks to be an impressive premium tool for organizations or businesses that want to use Linux while also being able to maintain their systems easily.
|
||||
|
||||
Personally, I wouldn’t mind paying for the service if it makes deploying and using Linux easier, in general.
|
||||
|
||||
Of course, it does not support every Linux distro as of yet – but it is indeed a promising service to keep an eye out for.
|
||||
|
||||
What do you think about it? Do you know of a better alternative to Zorin Grid? Do share your views in the comments.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/zorin-grid/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://zorinos.com/grid/
|
||||
[2]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/01/zorin-grid-dashboard.png?ssl=1
|
||||
[3]: https://itsfoss.com/vicenza-windows-zorin/
|
||||
[4]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/01/zorin_grid_features.jpg?ssl=1
|
||||
[5]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/01/zorin-os-computers.jpg?ssl=1
|
||||
[6]: https://itsfoss.com/zorin-os-interview/
|
||||
[7]: https://zorinos.com/help/switch-your-organization-to-zorin-os/
|
||||
@@ -1,141 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Learning about Partitions and How to Create Them for Fedora)
|
||||
[#]: via: (https://fedoramagazine.org/learning-about-partitions-and-how-to-create-them-for-fedora/)
|
||||
[#]: author: (Gregory Bartholomew https://fedoramagazine.org/author/glb/)
|
||||
|
||||
Learning about Partitions and How to Create Them for Fedora
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
Operating system distributions try to craft a one size fits all partition layout for their file systems. Distributions cannot know the details about how your hardware is configured or how you use your system though. Do you have more than one storage drive? If so, you might be able to get a performance benefit by putting the write-heavy partitions (_var_ and _swap_ for example) on a separate drive from the others that tend to be more read-intensive since most drives cannot read and write at the same time. Or maybe you are running a database and have a small solid-state drive that would improve the database’s performance if its files are stored on the SSD.
|
||||
|
||||
The following sections attempt to describe in brief some of the historical reasons for separating some parts of the file system out into separate partitions so that you can make a more informed decision when you install your Linux operating system.
|
||||
|
||||
If you know more (or contradictory) historical details about the partitioning decisions that shaped the Linux operating systems used today, contribute what you know below in the comments section!
|
||||
|
||||
### Common partitions and why or why not to create them
|
||||
|
||||
#### The boot partition
|
||||
|
||||
One of the reasons for putting the ***/*_boot_ directory on a separate partition was to ensure that the boot loader and kernel were located within [the first 1024 cylinders of the disk][2]. Most modern computers do not have the 1024 cylinder restriction. So for most people, this concern is no longer relevant. However, modern UEFI-based computers have [a different restriction][3] that makes it necessary to have a separate partition for the boot loader. UEFI-based computers require that the boot loader ([which can be the Linux kernel directly][4]) be on a FAT-formatted file system. The Linux operating system, however, requires a POSIX-compliant file system that can designate access permissions to individual files. Since [FAT file systems do not support access permissions][5], the boot loader must be on a separate file system than the rest of the operating system on modern UEFI-based computers. A single partition cannot be [formatted][6] with more than one type of file system.
|
||||
|
||||
#### The var partition
|
||||
|
||||
One of the historical reasons for [putting the /var directory on a separate partition][7] was to prevent files that were frequently written to (_/var/log/*_ for example) from filling up the entire drive. Since modern drives tend to be much larger and since other means like [log rotation][8] and [disk quotas][9] are available to manage storage utilization, putting _/var_ on a separate partition may not be necessary. It is much easier to change a disk quota than it is to re-partition a drive.
|
||||
|
||||
Another reason for isolating _/var_ was that file system corruption was much more common in the original version of the Linux [Extended File System (EXT)][10]. The file systems that had more write activity were much more likely to be irreversibly corrupted by a power outage than those that did not. By partitioning the disk into separate file systems, one could limit the scope of the damage in the event of file system corruption. This concern is no longer as significant because modern file systems support [journaling][11].
|
||||
|
||||
#### The home partition
|
||||
|
||||
Having ***/*_home_ on a separate partition makes it possible to re-format the other partitions without overwriting your home directories. However, because modern Linux distributions are much better at doing in-place operating system upgrades, re-formatting shouldn’t be needed as frequently as it might have been in the past.
|
||||
|
||||
It can still be useful to have _/home_ on a separate partition if you have a dual-boot setup and want both operating systems to share the same home directories. Or if your operating system is installed on a file system that supports snapshots and rollbacks and you want to be able to rollback your operating system to an older snapshot without reverting the content in your user profiles. Even then, some file systems allow their descendant file systems to be rolled back independently, so it still may not be necessary to have a separate partition for _/home_. On ZFS, for example, one pool/partition can have multiple descendant file systems.
|
||||
|
||||
#### The swap partition
|
||||
|
||||
The _swap_ partition reserves space for the contents of RAM to be written to permanent storage. There are pros and cons to having a swap partition. A pro of having swap memory is that it theoretically gives you time to gracefully shutdown unneeded applications before [the OOM killer][12] takes matters into its own hands. This might be important if the system is running mission-critical software that you don’t want abruptly terminated. A con might be that your system runs so slow when it starts swapping memory to disk that you’d rather the OOM killer take care of the problem for you.
|
||||
|
||||
Another use for swap memory is [hibernation mode][13]. This might be where the rule that the swap partition should be twice the size of your computer’s RAM originated. Ideally, you should be able to put a system into hibernation even if nearly all of its RAM is in use. Beware that Linux’s support for hibernation is not perfect. It is not uncommon that after a Linux system is resumed from hibernation some hardware devices are left in an inoperable state (for example, no video from the video card or no internet from the WiFi card).
|
||||
|
||||
In any case, having a swap partition is more a matter of taste. It is not required.
|
||||
|
||||
#### The root partition
|
||||
|
||||
The _root_ partition (/) is the catch-all for all directories that have not been assigned to a separate partition. There is always at least one root partition. BIOS-based systems that are new enough to not have the 1024 cylinder limit can be configured with only a root partition and no others so that there is never a need to resize a partition or file system if space requirements change.
|
||||
|
||||
#### The EFI system partition
|
||||
|
||||
The [EFI System Partition (ESP)][14] serves the same purpose on UEFI-based computers as the _boot_ partition did on the older BIOS-based computers. It contains the boot loader and kernel. Because the files on the ESP need to be accessible by the computer’s firmware, the ESP has a few restrictions that the older boot partition did not have. The restrictions are:
|
||||
|
||||
1. The ESP must be formatted with a FAT file system (_vfat_ in Anaconda)
|
||||
2. The ESP must have a special [type-code][15] (_EF00_ when using [gdisk][16])
|
||||
|
||||
|
||||
|
||||
Because the older boot partition did not have file system or type-code restrictions, it is permissible to apply the above properties to the _boot_ partition and use it as your ESP. Note, however, that the GRUB boot loader does not support combining the boot and ESP partitions. If you use GRUB, you will have to create a separate partition and mount it beneath the _/boot_ directory.
|
||||
|
||||
The [Boot Loader Specification (BLS)][17] lists several reasons why it is ideal to use the legacy boot partition as your ESP. The reasons include:
|
||||
|
||||
1. The UEFI firmware should be able to load the kernel directly. Having a separate, non-ESP compliant boot partition for the kernel prevents the UEFI firmware from being able to directly load the kernel.
|
||||
2. Nesting the ESP mount point three mount levels deep increases the likelihood that an intermediate mount could fail or otherwise be unavailable when needed. That is, requiring _root_ (/), then _boot_ (/boot), then _efi_ (/efi) to be consecutively mounted is unnecessarily complex and prone to error.
|
||||
3. Requiring the boot loader to be able to read other partitions/disks which may be formatted with arbitrary file systems is non-trivial. Even when the boot loader does contain such code, the code that works at installation time can become outdated and fail to access the kernel/initrd after a file system update. This is currently true of GRUB’s _ZFS_ file system driver, for example. You must be careful not to update your _ZFS_ file system if you use the GRUB boot loader or else your system may not come back up the next time you reboot.
|
||||
|
||||
|
||||
|
||||
Besides the concerns listed above, it is a good idea to have your startup environment — up to and including your [initramfs][18] — on a single self-contained file system for recovery purposes. Suppose, for example, that you need to rollback your root file system because it has become corrupted or it has become infected with malware. If your _kernel_ and _initramfs_ are on the _root_ file system, you may be unable to perform the recovery. By having the _boot loader_, _kernel_, and _initramfs_ all on a single file system that is rarely accessed or updated, you can increase your chances of being able to recover the rest of your system.
|
||||
|
||||
In summary, there are many ways that you can layout your partitions and the type of hardware (BIOS or UEFI) and the brand of boot loader (GRUB, Syslinux or [systemd-boot][19]) are among the factors that will influence which layouts will work.
|
||||
|
||||
### Other considerations
|
||||
|
||||
#### MBR vs. GPT
|
||||
|
||||
[GUID Partition Table (GPT)][20] is the newer partition format that supports larger disks. GPT was designed to work with the newer UEFI firmware. It is backward-compatible with the older [Master Boot Record (MBR)][21] partition format but not all boot loaders support the [MBR boot method][21]. _GRUB_ and _Syslinux_ support both MBR and UEFI, but _systemd-boot_ only supports the newer [UEFI boot method][22].
|
||||
|
||||
By using GPT now, you can increase the likelihood that your storage device, or an image of it, can be transferred over to a newer computer in the future should you wish to do so. If you have an older computer that natively supports only MBR-partitioned drives, you may need to add the _inst.gpt_ parameter to **Anaconda** when starting the installer to get it to use the newer format. How to add the _inst.gpt_ parameter is shown in the below video titled **“Partitioning a BIOS Computer”**.
|
||||
|
||||
If you use the _GPT_ partition format on a BIOS-based computer, and you use the _GRUB_ boot loader, you must additionally create a one megabyte _biosboot_ partition at the start of your storage device. The _biosboot_ partition is not needed by any other brand of boot loader. How to create the _biosboot_ partition is demonstrated in the below video titled **“Partitioning a BIOS Computer”**.
|
||||
|
||||
#### LVM
|
||||
|
||||
One last thing to consider when manually partitioning your Linux system is whether to use standard partitions or logical volumes. Logical volumes are managed by the [Logical Volume Manager (LVM)][23]. You can setup LVM volumes directly on your disk without first creating standard partitions to hold them. However, most computers still require that the boot partition be a standard partition and not an LVM volume. Consequently, having LVM volumes only increases the complexity of the system because the LVM volumes must be created within standard partitions.
|
||||
|
||||
The main features of LVM — online storage resizing and clustering — are not really applicable to the typical end user. Most laptops do not have hot-swappable drive bays for adding or reconfiguring storage while the system is running. And not many laptop or desktop users have [clvmd][24] configured so they can access a centralized storage device concurrently from multiple client computers.
|
||||
|
||||
LVM is great for servers and clusters. But it adds extra complexity for the typical end user. Go with standard partitions unless you are a server admin who needs the more advanced features.
|
||||
|
||||
### Video demonstrations
|
||||
|
||||
Now that you know which partitions you need, you can watch the sort video demonstrations below to see how to manually partition a Fedora Linux computer from the Anaconda installer.
|
||||
|
||||
These videos demonstrate creating only the minimally required partitions. You can add more if you choose.
|
||||
|
||||
Because the _GRUB_ boot loader requires a more complex partition layout on UEFI systems, the below video titled **“Partitioning a UEFI Computer”** additionally demonstrates how to install the _systemd-boot_ boot loader. By using the _systemd-boot_ boot loader, you can reduce the number of needed partitions to just two — _boot_ and _root_. How to use a boot loader other than the default (GRUB) with Fedora’s Anaconda installer is officially documented [here][25].
|
||||
|
||||
**Partitioning a UEFI Computer**
|
||||
|
||||
**Partitioning a BIOS Computer**
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/learning-about-partitions-and-how-to-create-them-for-fedora/
|
||||
|
||||
作者:[Gregory Bartholomew][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/glb/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2020/01/partitions-fedora-816x346.png
|
||||
[2]: https://en.wikipedia.org/wiki/Cylinder_1024
|
||||
[3]: https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface#EFI_system_partition
|
||||
[4]: https://www.kernel.org/doc/Documentation/efi-stub.txt
|
||||
[5]: https://en.wikipedia.org/wiki/File_system_permissions#Operating_system_variations
|
||||
[6]: https://en.wikipedia.org/wiki/Disk_formatting#Formatting
|
||||
[7]: https://access.redhat.com/discussions/641923
|
||||
[8]: https://linuxconfig.org/setting-up-logrotate-on-redhat-linux
|
||||
[9]: https://docs.oracle.com/cd/E19253-01/819-5461/gazud/index.html
|
||||
[10]: https://en.wikipedia.org/wiki/Extended_file_system
|
||||
[11]: https://en.wikipedia.org/wiki/Journaling_file_system
|
||||
[12]: https://lwn.net/Articles/317814/
|
||||
[13]: https://www.fosslinux.com/184/what-is-difference-between-suspend-and-hibernate-in-linux.htm
|
||||
[14]: https://en.wikipedia.org/wiki/EFI_system_partition
|
||||
[15]: https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
|
||||
[16]: https://fedoramagazine.org/managing-partitions-with-sgdisk/
|
||||
[17]: https://systemd.io/BOOT_LOADER_SPECIFICATION/
|
||||
[18]: https://fedoramagazine.org/initramfs-dracut-and-the-dracut-emergency-shell/
|
||||
[19]: https://www.freedesktop.org/wiki/Software/systemd/systemd-boot/
|
||||
[20]: https://en.wikipedia.org/wiki/GUID_Partition_Table
|
||||
[21]: https://en.wikipedia.org/wiki/Master_boot_record
|
||||
[22]: https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface#UEFIBOOT
|
||||
[23]: https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)
|
||||
[24]: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/logical_volume_manager_administration/lvm_cluster_overview
|
||||
[25]: https://docs.fedoraproject.org/en-US/fedora/rawhide/install-guide/install/Installing_Using_Anaconda/#sect-installation-gui-storage-partitioning-bootloader
|
||||
@@ -1,96 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Thunderbolt – how to use keyboard during boot time)
|
||||
[#]: via: (https://fedoramagazine.org/thunderbolt-how-to-use-keyboard-during-boot-time/)
|
||||
[#]: author: (Martin Sehnoutka https://fedoramagazine.org/author/msehnout/)
|
||||
|
||||
Thunderbolt – how to use keyboard during boot time
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
### Problem statement
|
||||
|
||||
Imagine you bought a new laptop with a shiny new USB-C docking station. You install fresh Fedora, encrypt your hard drive because laptop is a travel equipment and you do not want to travel around with non-ecrypted hard drive. You finish the installation, close the lid because you have external monitor, reboot the machine, and finally you would like to enter the LUKS password using the external keyboard attached using USB 2.0 to the USB-C docking station but it does not work!
|
||||
|
||||
The keyboard does not respond at all. So you open the lid, try the built-in keyboard which works just fine and once the machine boots the external keyboard works just fine as well. What is the problem?
|
||||
|
||||
### What is this Thunderbolt anyway and why would anyone want it?
|
||||
|
||||
Thunderbolt is a hardware interface to connect peripherals such as monitors, external network cards [1] or even graphic cards [1]. The physical connector is the same as USB-C, but there is usually a label with a little lightning right next to the port to differentiate “plain” USB-C from Thunderbolt ports.
|
||||
|
||||
![][2]
|
||||
|
||||
Of course it comes with very high transmission speed to support such demanding peripherals, but it also comes with a certain security risks. To achieve transmission speed like this, Thunderbolt uses Direct Memory Access (DMA) for the peripheral devices. As the name suggests, this method allows the external device to read and write memory directly without talking to the running operating system.
|
||||
|
||||
I guess you can already spot the problem here. If some stranger is walking around my laptop (even with the screen locked), is it really possible to just attach a device and read content of my computer memory? Let’s discuss it in more detail.
|
||||
|
||||
### User facing solution for Thunderbolt security
|
||||
|
||||
In the recent versions, Gnome settings include a tab for Thunderbolt device configuration. You can enable and disable DMA access for external devices and you can also verify identity of the devices.
|
||||
|
||||
![][3]
|
||||
|
||||
_bolt_ is the component responsible for managing thunderbolt devices. See _man 8 boltd_ for more information.
|
||||
|
||||
### CLI tools
|
||||
|
||||
Of course it is possible to control the same via command line. I suggest you to read _man boltctl_ or check the upstream repository directly: <https://gitlab.freedesktop.org/bolt/bolt>
|
||||
|
||||
### Pre-boot support – solution to the keyboard problem
|
||||
|
||||
In pre-boot environment, the situation is slightly different. The userspace service responsible for device verification is not yet running so if a device is to be allowed, the firmware must to it. In order to enable this feature go to your BIOS and look for “support in pre boot environment”. For example this is how it looks on a Lenovo laptop:
|
||||
|
||||
![][4]
|
||||
|
||||
Once you enable this feature, bolt will add any verified device to a list of allowed devices. The next time you boot your machine, you should be able to use your external keyboard.
|
||||
|
||||
Run _boltctl_ a look for “bootacl”. Make sure that the list of allowed devices contains the one you wish to use.
|
||||
|
||||
![][5]
|
||||
|
||||
Also note the “security: secure” line. If you see anything else, for instance “security: user” I recommend to reconfigure BIOS.
|
||||
|
||||
### Technical details of the pre-boot support
|
||||
|
||||
There is one unfortunate technical detail about this solution. Thunderbolt support different security levels. For running Fedora, I recommend you to use “secure” level to verify that the device is indeed the one that it claims to be by using a per-device key generated by the host and stored in the device. Firmware, on the other hand, will only use “user” level which uses simple UUID that is provided by the device. The difference is that a malicious device could claim to be a different one by providing the same UUID as a legitimate one. Anyway this should not be a problem as the memory does not contain any sensitive data yet.
|
||||
|
||||
You can find more technical details in this blog post: <https://christian.kellner.me/2019/02/11/thunderbolt-preboot-access-control-list-support-in-bolt/>
|
||||
|
||||
### Conclusion
|
||||
|
||||
As you can see, in recent enough Fedora version the solution is a simple switch in BIOS. So if you are still opening your laptop during boot, go ahead and configure it so you don’t have to do it next time. Meanwhile **check that the default security level is “secure”** instead of “user” [5].
|
||||
|
||||
### Sources:
|
||||
|
||||
[1] <https://www.intel.com/content/www/us/en/products/docs/io/thunderbolt/thunderbolt-technology-developer.html>
|
||||
|
||||
[2] <https://christian.kellner.me/2019/02/11/thunderbolt-preboot-access-control-list-support-in-bolt/>
|
||||
|
||||
[3] <https://gitlab.freedesktop.org/bolt/bolt>
|
||||
|
||||
[4] <https://wiki.gnome.org/Design/Whiteboards/ThunderboltAccess>
|
||||
|
||||
[5] <https://christian.kellner.me/2019/02/27/thunderclap-and-linux/>
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/thunderbolt-how-to-use-keyboard-during-boot-time/
|
||||
|
||||
作者:[Martin Sehnoutka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/msehnout/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2020/01/header-816x345.png
|
||||
[2]: https://fedoramagazine.org/wp-content/uploads/2020/01/port-1024x165.jpg
|
||||
[3]: https://fedoramagazine.org/wp-content/uploads/2020/01/Screenshot-from-2020-01-17-12-47-30.png
|
||||
[4]: https://fedoramagazine.org/wp-content/uploads/2020/01/bios-1024x389.jpg
|
||||
[5]: https://fedoramagazine.org/wp-content/uploads/2020/01/Screenshot-from-2020-01-17-13-18-21.png
|
||||
@@ -1,80 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Oh, Bummer! Rocket League is Ending Support For Linux)
|
||||
[#]: via: (https://itsfoss.com/rocket-league-ending-support-for-linux/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
Oh, Bummer! Rocket League is Ending Support For Linux
|
||||
======
|
||||
|
||||
If you’ve enjoyed playing Rocket League on Linux, you will be disappointed to know that [Pysonix][1], the developer team behind Rocket League [announced][2] that they will be dropping support for Linux and Mac in March, 2020.
|
||||
|
||||
If it was just another casual game on [Steam][3], I wouldn’t mind- but Rocket League is a quite popular online multiplayer game across every platform.
|
||||
|
||||
![][4]
|
||||
|
||||
In fact, Rocket League was one of my [favorite games to play on Linux][5] (in addition to CS: GO). Even though I haven’t played it for a while – it is a bummer that I won’t be able to play it either.
|
||||
|
||||
So, this is definitely sad for Linux gamers who were looking forward to having fun in a popular online multiplayer game that required a decent hardware configuration to work flawlessly.
|
||||
|
||||
### Why are they ending support?
|
||||
|
||||
![][6]
|
||||
|
||||
In their [announcement][2], they mentioned:
|
||||
|
||||
> As we continue to upgrade _**Rocket League**_ with new technologies, it is no longer viable for us to maintain support for the macOS and Linux (SteamOS) platforms. As a result, the final patch for the macOS and Linux versions of the game will be in March. This update will disable online functionality (such as in-game purchases) for players on macOS and Linux, but offline features including Local Matches, and splitscreen play will still be accessible.
|
||||
|
||||
Well, this certainly does not explain why they’re dropping support for Linux/Mac. But, it looks like the game will get its final patches in March.
|
||||
|
||||
**After that, you will not be able to play multiplayer sessions – but will be restricted to the local multiplayer sessions (or split-screen)**.
|
||||
|
||||
Maybe you can try using [Wine][7] or [Steam Play][8] to play it on Linux? Doesn’t sound good though.
|
||||
|
||||
Some furious users/gamers on [Reddit][9] mentioned that this is a result of Epic Games acquiring Rocket League developer **Psyonix**. I wouldn’t comment on that one – feel free to share your thoughts in the comments though.
|
||||
|
||||
### How to get a refund for your Rocket League purchase
|
||||
|
||||
![][10]
|
||||
|
||||
To get a refund for your purchase of the Rocket League, you need to open a ticket on the [Psyonix Support][11] page.
|
||||
|
||||
If you’ve purchased it recently in the ongoing Steam sale – you might just get an instant refund from Steam if you initiate a refund.
|
||||
|
||||
If you have no plans to play it on a Windows machine (or trying Proton/Wine) on Linux – you should apply for a refund.
|
||||
|
||||
**Wrapping Up**
|
||||
|
||||
While this may not be a big-shot game for the platform but dropping support for Linux is not helping to improve the gaming scene on Linux.
|
||||
|
||||
If a game that worked quite well on Linux drops support for it – how can we expect newer games to consider adding support for Linux?
|
||||
|
||||
Feel free to share your thoughts in the comments down below.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/rocket-league-ending-support-for-linux/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.psyonix.com/
|
||||
[2]: https://www.rocketleague.com/news/ending-support-for-mac-and-linux/
|
||||
[3]: https://store.steampowered.com/app/252950/Rocket_League/
|
||||
[4]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/01/rocket-league.jpg?ssl=1
|
||||
[5]: https://itsfoss.com/free-linux-games/
|
||||
[6]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/01/rocket-league-shot.jpg?ssl=1
|
||||
[7]: https://itsfoss.com/use-windows-applications-linux/
|
||||
[8]: https://itsfoss.com/steam-play/
|
||||
[9]: https://www.reddit.com/r/linux/comments/esxil2/support_for_rocket_league_on_linux_is_ending/
|
||||
[10]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/01/rocket-league-refund.jpg?ssl=1
|
||||
[11]: https://support.rocketleague.com/hc/en-us
|
||||
@@ -1,69 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Building Zero Trust authentication for multi-cloud application services)
|
||||
[#]: via: (https://www.linux.com/articles/building-zero-trust-authentication-for-multi-cloud-application-services/)
|
||||
[#]: author: (Swapnil Bhartiya https://www.linux.com/author/swapnil/)
|
||||
|
||||
Building Zero Trust authentication for multi-cloud application services
|
||||
======
|
||||
|
||||
[![][1]][2]
|
||||
|
||||
[![][1]][2]
|
||||
|
||||
One of the fundamental challenges organizations have about multi-cloud and hybrid cloud environments, is how to easily establish secure communication across different clouds and environments. Cloud providers have their own identity and access management solutions, such as AWS IAM, to manage what access an instance should and should not have. But as soon as the applications or services need to communicate from AWS to GCP or from AWS to their on-prem infrastructure, it becomes a challenge because it’s AWS-specific and not interoperable. Engineering and operations teams need something secure that could work across environments and at the same time should not add any friction to the deployment cycles
|
||||
|
||||
This is the problem [Scytale][3], a is trying to address with Secure Production Identity Framework for Everyone ([SPIFFE][4]) and SPIFFE Runtime Environment ([SPIRE][5]). Both of these open-source projects originated at Scytale but now are part of the Cloud Native Computing Foundation (CNCF). These projects have grown in popularity within the cloud native community and have seen contributions from organizations such as Amazon, Bloomberg, Google, Pinterest, Square , Uber and more.
|
||||
|
||||
“Scytale is the primary driver of these projects that offer ‘interoperable identity’ between different cloud providers and different platforms,” Evan Gilman, Senior Engineer at Scytale.io and co-author of _[Zero Trust Networks][6]_. “From the commercial angle, we have built solutions to help organizations adopt these projects faster and extend their functionalities to address the needs of enterprise customers .”
|
||||
|
||||
**Vendor and technology neutral identity solution**
|
||||
The passport analogy best explains interoperable identity. Passports from different countries all look different, but they all have the same size and meet the same specifications. They all have a picture of the passport holder at the same spot, they all have a barcode at the bottom. Regardless of what country issued the passport, it works across the globe.
|
||||
|
||||
A “country” can be a particular software stack, platform, or a cloud provider. Regardless of the environment, the identities that exist within and between those silos can communicate.
|
||||
|
||||
Interoperable identity becomes even more critical in the multi-cloud and hybrid cloud deployments, as they raise this fundamental challenge of how users secure communication across those boundaries.
|
||||
|
||||
“We are bringing in a platform-agnostic service identity that is not specific to a cloud provider, platform, and technology,” said Gilman. It levels the playing field and allows users to talk across boundaries. Users won’t talk in AWS or GCP specifics; they communicate on the SPIFFE level. “SPIFFE provides users with what is sometimes referred to as a secure dial tone: you pick up the phone, it rings the other side irrespective of where it’s running and what platform it’s running on,” added Gilman.
|
||||
|
||||
**SPIFFE based service** authentication **foundational for zero trust networks**
|
||||
SPIFFE is a standard, a set of documents whereas SPIRE is the software implementation of that standard. SPIRE implements the SPIFFE specifications and enables workloads or services to get these “passports” as soon as they boot, in a way that is very reliable, scalable, and highly automated. This identity centric authentication is also critical for building a zero trust-based security model , which removes reliance on networks to deliver trustworthy information.
|
||||
|
||||
“Networks have been historically fairly manipulable. So instead we build systems in such a way that it doesn’t rely on that network to deliver trustworthy information,” said Gilman, “We use protocols and strong authentication and authorization to try to mitigate any kind of business that might happen on the wire. It also mitigates what we call lateral movement. So if a neighbor is compromised, just because you’re attached to the same network, that should not mean that you should gain access that you would not have otherwise.”
|
||||
|
||||
Gilman explains, “Part of the SPIFFE specification set deals with what we call ‘federation’. There is usually a centralized authority that issues these identities. In reality, there are different companies that have their own authorities. Even different software stacks have their own authorities. There is a need to bridge these gaps.”
|
||||
|
||||
That’s where the SPIFFE Federation enters the picture. It swaps these cryptographic keys between different domains. It allows users with different identity providers to communicate effortlessly.
|
||||
|
||||
One key design principle of the SPIFFE Federation is that it is compatible with OIDC, which is a similar identity federation spec, but is more focused around users. It allows for server-to-server and service-to-service communication. Any existing OIDC can take advantage of it and pass one of its SPIFFE identity documents to a public cloud like AWS, which will be able to validate it using this OIDC SPIFFE Federation mechanism.
|
||||
|
||||
While SPIFFE as a specification doesn’t change, SPIRE has a monthly release cadence. It continues to add new features on a regular basis.
|
||||
|
||||
The latest release introduced integration with the AWS Private CA Manager, which means that SPIRE deployments living inside AWS can use it to protect the sign-in keys for identities. These identities are cryptographically backed so there is a key that is used to sign these identities. One of the biggest challenges is to secure these sign-in keys. Being able to bury that key inside the AWS service, which is backed by hardware protection, is an incredible feature.
|
||||
|
||||
The community is also working on a feature called Nested SPIRE, which allows users to have multiple SPIRE server clusters that form a tree and chain up to each other.
|
||||
|
||||
Together, these new features give a lot of flexibility in terms of architecting for failure modes and failure domains, and architecting around different security domains.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/articles/building-zero-trust-authentication-for-multi-cloud-application-services/
|
||||
|
||||
作者:[Swapnil Bhartiya][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.linux.com/author/swapnil/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.linux.com/wp-content/uploads/2020/01/buffer-1143485_1920-1068x638.jpg (passport)
|
||||
[2]: https://www.linux.com/wp-content/uploads/2020/01/buffer-1143485_1920.jpg
|
||||
[3]: https://scytale.io/
|
||||
[4]: https://spiffe.io/
|
||||
[5]: https://spiffe.io/spire/
|
||||
[6]: https://www.amazon.com/Zero-Trust-Networks-Building-Untrusted/dp/1491962194
|
||||
@@ -1,77 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to get started with test-driven development)
|
||||
[#]: via: (https://opensource.com/article/20/1/test-driven-development)
|
||||
[#]: author: (Alex Bunardzic https://opensource.com/users/alex-bunardzic)
|
||||
|
||||
How to get started with test-driven development
|
||||
======
|
||||
Learn when, what, and how to test in a TDD system.
|
||||
![Penguin driving a car with a yellow background][1]
|
||||
|
||||
I am often approached by software developers who are on board with the switch to test-driven development (TDD). They understand that describing expectations first and then writing code to meet those expectations is the best way to write software. And they agree that writing tests first does not introduce any overhead since they must write tests anyway. Still, they find themselves stuck, not being clear on what to test, when to test it, and how to test it. This article will answer those questions.
|
||||
|
||||
### First, an analogy
|
||||
|
||||
Imagine you're working on a team that has been asked to build a race car. The goal is to deliver a product that will enable a crew to drive the car from one city (say, Portland, Oregon) to another city (say, Seattle, Washington).
|
||||
|
||||
Your team could go about designing and building that car in several different ways. One way would be to handcraft a unique, monolithic vehicle where all parts are home-grown and tightly coupled. Another way would be to use only prefabricated parts and stitch them together. And there are many other permutations of these two extreme approaches.
|
||||
|
||||
Suppose your team goes with hand-building the constituent components of the race car. A car needs a battery to run. For the purposes of this analogy, focus on the custom-made car battery. How would you go about testing it?
|
||||
|
||||
### Testing strategies
|
||||
|
||||
One way to the test custom-made car battery would be to hire a testing crew, ship the car with the battery to Portland, and then get the testing crew to drive the car from Portland to Seattle. If the car arrives in Seattle, you can confirm that, yes, the car battery functions as expected.
|
||||
|
||||
Another way to test the custom-made car battery would be to install it in the car and see if the engine turns over. If the engine starts, you can confirm that, yes, the car battery functions as expected.
|
||||
|
||||
Still another way would be to use a voltmeter and connect the positive (+) and the negative (-) terminals to see if the voltmeter registers voltage output in the range of 12.6 to 14.7 volts. If it does, you can confirm that, yes, the car battery functions as expected.
|
||||
|
||||
The above three hypothetical examples illustrate how different ways of testing the car battery align with three categories of testing strategies:
|
||||
|
||||
1. Employing the testing crew to drive the car from Portland to Seattle aligns with the **system or end-to-end testing strategy**.
|
||||
2. Installing the battery in the car and verifying if the engine starts aligns with the **integration testing strategy**.
|
||||
3. Measuring the voltage output of the car battery to verify if it falls within the expected range aligns with the **unit testing strategy**.
|
||||
|
||||
|
||||
|
||||
### TDD is all about unit testing
|
||||
|
||||
I hope these examples provide simple guiding principles for discerning between unit, integration, and system end-to-end testing.
|
||||
|
||||
Keeping those guidelines in mind, it is very important _never_ to include integration nor system tests in your TDD practice. In TDD, the expected outcomes are always micro-outcomes. Measuring the voltage output of a car battery is a good example of a micro-outcome. A car battery is a unit of functionality that cannot easily be broken down into a few smaller units of functionality. As such, it is a perfect candidate for writing a unit test (i.e., describing the expected measurable output).
|
||||
|
||||
You could also write a description of your expectations in the form of: "I expect the car engine to start on the event of turning the key." However, that description wouldn't qualify as a unit test. Why? Because the car is not at a sufficiently low level of granularity. In software engineering parlance, the car does not embody the [single responsibility principle][2] (SRP).
|
||||
|
||||
And of course, while you could also write a description of your expectation in the form of: "I expect the car, which begins its journey in Portland, to arrive in Seattle after x number of hours," that description wouldn't qualify as a unit test. Many aspects of the car's journey from Portland to Seattle could be measured, so such end-to-end descriptions should never be part of TDD.
|
||||
|
||||
### Simulating real conditions
|
||||
|
||||
In the case of a car battery, just by using a simple voltmeter, you can simulate the operational environment of a car battery. You don't have to go into the expense of providing a full-blown experience (e.g., a fully functional car, a long and treacherous trip from Portland to Seattle) to be convinced that, indeed, your car battery functions as expected.
|
||||
|
||||
That's the beauty of unit testing's simplicity. It's easy to simulate, easy to measure, easy to leave the exercise being convinced that everything works as expected.
|
||||
|
||||
So what is it that enables this magic? The answer is simple—the _absence of dependencies_. A car battery does not depend on anything related to the automobile. Nor does it depend on anything related to the road trip from Portland to Seattle. Keep in mind that as your decomposed system components become less and less dependent on other components, your solution gets more and more reliable.
|
||||
|
||||
### Conclusion
|
||||
|
||||
The art of software engineering consists of the ability to decompose complex systems into small constituent elements. Each individual element must be reduced to the smallest possible surface. Once you reach that point in your process of decomposing a system, you can quite easily focus your attention on describing your expectations about the output of each unit. You can do that by following a formalized pattern, in which you first describe the _preconditions_ (i.e., given that such-and-such values are present), the _action_ (i.e., given that such-and-such _event_ arrives), and the _outcome_ or the _post-condition_ (i.e., you expect such-and-such values to be measurable).
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/test-driven-development
|
||||
|
||||
作者:[Alex Bunardzic][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/alex-bunardzic
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/car-penguin-drive-linux-yellow.png?itok=twWGlYAc (Penguin driving a car with a yellow background)
|
||||
[2]: https://en.wikipedia.org/wiki/Single_responsibility_principle
|
||||
@@ -1,68 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How I had a nerdy date night with StreetComplete quests)
|
||||
[#]: via: (https://opensource.com/article/20/1/streetcomplete-crowdsource-maps)
|
||||
[#]: author: (Jess Weichler https://opensource.com/users/cyanide-cupcake)
|
||||
|
||||
How I had a nerdy date night with StreetComplete quests
|
||||
======
|
||||
Find an adventure in your own backyard with this fun app.
|
||||
![A map with a route highlighted][1]
|
||||
|
||||
StreetComplete is an Android app that makes it fun and easy to contribute to open data by completing quests.
|
||||
|
||||
Quests are used to fill in incomplete or inaccurate information on [OpenStreetMap][2], an open data project dedicated to mapping the world through crowdsourcing. Anyone can contribute to the map and, thanks to free culture and open source licenses, that data can then be used by anyone for anything, from video games to custom map applications and artwork.
|
||||
|
||||
![hands holding phone][3]
|
||||
|
||||
My first intro to StreetComplete was on a charmingly unique date night with my partner. Instead of dinner and a movie, we explored the streets of our town, both the familiar and unfamiliar, answering questions about features of topography, shopfronts, pedestrian crossing, and more. We got to see our town in a new light, and we felt we were ultimately helping others discover it along with us.
|
||||
|
||||
If this sounds like something you’d like to get involved with, it’s super simple to get started. Just follow the steps below:
|
||||
|
||||
1. [Download the app][4] to your phone from F-Droid or Google Play. It’s licensed under GPLv3.
|
||||
2. Allow StreetComplete to access your device’s location.
|
||||
3. Click the menu in the upper right corner, and select Settings.
|
||||
4. In the Settings menu, select Authorize OSM access. This will open OpenStreetMap in your browser, where you can register for an account or login if you already have one.
|
||||
5. Authorize access to your account.
|
||||
|
||||
|
||||
|
||||
Now you’re ready to go!
|
||||
|
||||
### Completing quests
|
||||
|
||||
To complete quests, walk around the area of your choosing. Quests appear as pins on the map. Symbols inside pins denote the type of task, such as a wheelchair for an accessibility quest or leaf for a plant-based quest. Quests involve simple questions about various features, with clear multiple choice answers and visuals when necessary.
|
||||
|
||||
Quests can be chosen at random, though the app will question your response if you try to complete a quest when you are not nearby. I live in a small town, so I especially enjoyed quests that required us to ask questions of shop owners because it allowed us to connect with the community in real life while contributing to the global community online.
|
||||
|
||||
![StreetComplete map image][5]
|
||||
|
||||
If you live in a mobile deadzone or have a limited mobile plan, you can still have fun questing for map data; StreetComplete has an offline mode. To use it, activate the offline download option in the app settings menu. Once your region has downloaded, perform a search for nearby quests, then go out and explore.
|
||||
|
||||
When you are back online, the data you’ve input as quest responses are uploaded to the OpenStreetMap servers.
|
||||
|
||||
StreetComplete is a great way to investigate new areas or even locations you may already be somewhat familiar with. With its graphical, flat design and simple quests, it makes a perfect activity for couples, friends, and families to complete together.
|
||||
|
||||
Maps touch our lives daily. Whether you are trying to find a nearby point of interest or directions...
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/streetcomplete-crowdsource-maps
|
||||
|
||||
作者:[Jess Weichler][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/cyanide-cupcake
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/map_route_location_gps_path.png?itok=RwtS4DsU (A map with a route highlighted)
|
||||
[2]: https://www.openstreetmap.org
|
||||
[3]: https://opensource.com/sites/default/files/uploads/image1_street.png (hands holding phone)
|
||||
[4]: https://github.com/westnordost/StreetComplete
|
||||
[5]: https://opensource.com/sites/default/files/uploads/image2_street.jpeg (StreetComplete map image)
|
||||
@@ -1,175 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How I teach physics using open source tools)
|
||||
[#]: via: (https://opensource.com/article/20/1/teach-physics-open-source)
|
||||
[#]: author: (Cristiano L. Fontana https://opensource.com/users/cristianofontana)
|
||||
|
||||
How I teach physics using open source tools
|
||||
======
|
||||
A roundup of open source tools ideal for teaching physics (and other
|
||||
subjects).
|
||||
![Person reading a book and digital copy][1]
|
||||
|
||||
The nice aspect of being a physicist and a researcher is the openness of our community. There is a lot of collaboration and sharing of ideas (especially during coffee breaks). We also tend to share the software we write. Since we are very picky about algorithms, we want to modify other people’s code to fix the obvious errors that we find. It feels frustrating when I have to use proprietary tools since I cannot understand their inner workings. Having grown up professionally in such an environment, open source has been my go-to solution for all the software I use.
|
||||
|
||||
When I became the regular teacher of the Physics and Biophysics course at the [medical school][2] at my [university][3], I decided to use only open source software to prepare my lectures. Here is my experience so far and the solutions I found.
|
||||
|
||||
### Study material
|
||||
|
||||
Teaching is not easy. You should first understand the subject and then figure out how to communicate with somebody that knows nothing about the subject; therefore, it is of paramount importance to study the subject in depth and prepare the lectures well in advance. There are countless books about physics, but there are also some interesting, freely available resources. Most of these do not count as open source, as they cannot be modified, but they are useful anyways.
|
||||
|
||||
* [HyperPhysics][4] may have an outdated look, but it is a treasure trove of interesting concepts and insights by Carl R. Nave, Department of Physics and Astronomy Georgia State University.
|
||||
* [Open Source Physics][5] has a fabulous collection of applets and support material. I met some of the maintainers at various conferences, and they are remarkable people.
|
||||
* [OpenStax][6] is a nonprofit educational initiative based at Rice University that publishes textbooks that are free online. They have a good library and several works about physics in their [scientific section][7].
|
||||
* [Open Textbook Library][8] provides a catalog of free textbooks with a [physics section][9] as well. Some of the titles have reviews by users.
|
||||
* [Motion Mountain][10] is a collection of books about physics in general by C. Schiller that has also been translated into several languages.
|
||||
* [Light and Matter][11] is another collection of writings about different aspects of physics, all authored by B. Crowell of Fullerton College, CA.
|
||||
* [Wikipedia][12], what more can I say?
|
||||
|
||||
|
||||
|
||||
### Lecturing style
|
||||
|
||||
Before preparing my support material, I had to decide whether I preferred to use the blackboard or slideshows during the lectures. I opted to use both with the idea of showing hard-to-draw graphics in the slideshows and writing down equations on the blackboard. Eventually, the slideshows became much more prevalent. I use them as the draft of my lecture, helping me to keep track of what I want to say. I also added the mathematical proofs that I want to show to have a correct reference during the lecture.
|
||||
|
||||
Instead of using a blackboard, I ended up using a [graphics tablet][13] for all the notes I write during the lectures. I use the tablet for three main purposes: to draw additional drawings to explain myself better, to write down equations and proofs, and to write down the key messages that I want my students to remember. Even if what I write is already on the slideshows, actually writing it by hand during the lectures gives the students the time to write it down in their notes. After the lectures, I share, on my website, both the slideshows and my notes.
|
||||
|
||||
![Figure: Example of notes taken during class with the graphics tablet and Krita][14]
|
||||
|
||||
Figure: Example of notes taken during class with the graphics tablet and Krita
|
||||
|
||||
### Material preparation
|
||||
|
||||
#### Slideshows
|
||||
|
||||
Since math is the language of physics, I needed a practical way to write down equations in my slideshows. Probably, the best tool for that is [LaTeX][15], but it was designed to typeset books and not slides. Luckily, there is the [Beamer class][16] that allows you to typeset slideshows with LaTeX. The resulting file is a very portable PDF. The layout is nice and clean and forces me not to overstuff each slide. From the same source code, I can prepare two versions of the file. The lecture version follows the guidelines of the university’s [corporate identity][17] (_i.e.,_ with a thick red border), while the handout version has a cleaner layout (_e.g.,_ without the thick borders), thus allowing it to be printed without wasting printer toner. I have also seen students taking notes directly on the PDFs of the handouts on their tablets and computers.
|
||||
|
||||
The only drawback of using LaTeX and Beamer is the impossibility of embedding videos in the produced presentation. I, therefore, have to keep as a separate file the occasional videos that I show during class.
|
||||
|
||||
![Figure: Examples of slides created with LaTeX and Beamer][18]
|
||||
|
||||
Figure: Examples of slides created with LaTeX and Beamer
|
||||
|
||||
![Figure: Examples of slides created with LaTeX and Beamer][19]
|
||||
|
||||
Figure: Examples of slides created with LaTeX and Beamer
|
||||
|
||||
#### Images and diagrams
|
||||
|
||||
Something that I am careful about is the licensing of the graphics I use. As such, I never use graphics that do not allow me to redistribute my modifications. I drew most of the images in my slideshows. If I use other people’s work, I always reference it in my slide.
|
||||
|
||||
I prefer to use [vector graphics][20] whenever practicable because they can be easily modified and adjusted afterward. I use the exceptional [Inkscape][21] for my vector graphics. On the other hand, for my [raster graphics][22], I use [GIMP][23]. When I need a 3D looking diagram, I use [Blender][24] to draw the scene; then I trace the rendered image with Inkscape to convert it to vectorial. I recently discovered [FreeCAD][25], which has the striking feature that it can directly export the 3D scene to some vectorial format. I can then adjust the image with Inkscape without having to trace the raster image.
|
||||
|
||||
In all my diagrams, I am trying to keep a consistent look, and therefore I limit myself to a 10-[color palette][26], both from [d3][27] and [matplotlib][28]. I also use the same palette in my slides to highlight quantities in reference to the illustrations. To produce [plots][29] and [graphs][30], I write Python scripts and employ the matplotlib graphical library.
|
||||
|
||||
![Figure: Diagram example created with Inkscape by tracing a Blender 3D diagram][31]
|
||||
|
||||
Figure: Diagram example created with Inkscape by tracing a Blender 3D diagram
|
||||
|
||||
#### Multimedia support
|
||||
|
||||
I have prepared a few pages with applets that demonstrate some phenomena that I describe during my lectures (e.g., [modeling radioactive decay with dice][32]). I opted to offer these applets on my institutional webpage to easily reach all the students instead of requiring them to install software to run them. The necessary choice was to employ [JavaScript][33] and some supporting libraries, such as [jQuery][34] for compatibility between browsers, [MathJax][35] for typesetting math in the webpages, or [d3][27] for graphics display. Since my institution does not provide the capability of writing dynamic webpages, I am using [Jekyll][36], which is a static site generator. Jekyll allows me to have a consistent look and feel across all the pages without having to write HTML code.
|
||||
|
||||
![Figure: Dice modeling radioactive decay, image of the interactive simulation][37]
|
||||
|
||||
Figure: Dice modeling radioactive decay, image of the [interactive simulation][32]
|
||||
|
||||
### Lecturing software
|
||||
|
||||
Since my slideshows are PDF files, to show them during the lecture, I use the [default document viewer][38] of my [GNOME 3][39] desktop environment. There is also the alternative of using [Okular][40], which allows annotating the PDFs, but I prefer not to use it, as the annotations would not be carried over to the handouts. In conjunction with the graphical tablet, I use the excellent painting program [Krita][41]. I use the image layers as new pages of my notes. This approach is more practical during the lecture than creating a series of new files. I can also duplicate layers and edit them or load vectorial images and draw over them. Krita has the possibility of writing custom [Python plugins][42], so I prepared a script that exports a PDF file with a new page for each layer.
|
||||
|
||||
![Figure: Screenshot of Krita used in class to write notes][43]
|
||||
|
||||
Figure: Screenshot of Krita used in class to write notes
|
||||
|
||||
When we go through exercises in class, I use the calculator [Qalculate][44] to obtain numerical results. Its particular feature is that it can perform calculations with units associated with the numbers. I can focus more on the exercise resolution than on the units’ conversions. This is a double-edged sword, though, as the students would not learn how to do the conversions themselves. Therefore, I normally start to use Qalculate halfway through the course. [wxMaxima][45] can also support exercise resolution, symbolically solving some difficult equations.
|
||||
|
||||
![Figure: Screenshot of Qalculate][46]
|
||||
|
||||
Figure: Screenshot of Qalculate
|
||||
|
||||
### Video lectures
|
||||
|
||||
Sometimes I offer streaming lectures on YouTube, or I upload a video with additional mini-lectures. It has happened that the students have wanted a more in-depth explanation of some subjects. Offering these as a video allows them to take their time to listen and understand the subjects. For these videos, I use [OBS studio][47], which can record or directly stream videos to YouTube. OBS can put on the video scene the screen image and some additional video sources, such as a web camera. To edit the videos, I have been using [OpenShot][48].
|
||||
|
||||
![Figure: Screenshot of OBS studio recording the screen][49]
|
||||
|
||||
Figure: Screenshot of OBS studio recording the screen
|
||||
|
||||
### Conclusions
|
||||
|
||||
This is the set of open source tools that I have been using to prepare and support my lectures. Over the years, I changed some of them whenever I discovered some better fitting tools or if I changed my lecturing style. To be frank, due to laziness, one of the requirements for all the tools is that they have to be easily installable. I use [Fedora][50] on my laptop, and its repository has packages for all this software. On the [CentOS][51] installation of my desktop computer, I had worse luck. For instance, Krita and OBS are available only through [Flatpak][52].
|
||||
|
||||
Writing this article just made me realize how many tools I am actively using for my lectures. Maybe there are some all-in-one solutions, such as [LibreOffice Impress][53], but I am very satisfied with the results I am getting. Besides, all this software has other useful applications.
|
||||
|
||||
Leave comments if you have questions.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/teach-physics-open-source
|
||||
|
||||
作者:[Cristiano L. Fontana][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/cristianofontana
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/read_book_guide_tutorial_teacher_student_apaper.png?itok=_GOufk6N (Person reading a book and digital copy)
|
||||
[2]: https://www.medicinachirurgia.unipd.it/
|
||||
[3]: https://www.unipd.it/
|
||||
[4]: http://hyperphysics.phy-astr.gsu.edu/
|
||||
[5]: https://www.compadre.org/osp/
|
||||
[6]: https://openstax.org/
|
||||
[7]: https://openstax.org/subjects/science
|
||||
[8]: https://open.umn.edu/opentextbooks
|
||||
[9]: https://open.umn.edu/opentextbooks/subjects/physics
|
||||
[10]: http://www.motionmountain.net/
|
||||
[11]: http://www.lightandmatter.com/
|
||||
[12]: https://en.wikipedia.org/wiki/Main_Page
|
||||
[13]: https://en.wikipedia.org/wiki/Graphics_tablet
|
||||
[14]: https://opensource.com/sites/default/files/uploads/circuit_notes_0.jpg (Figure: Example of notes taken during class with the graphics tablet and Krita)
|
||||
[15]: https://www.latex-project.org/
|
||||
[16]: https://github.com/josephwright/beamer
|
||||
[17]: https://en.wikipedia.org/wiki/Corporate_identity
|
||||
[18]: https://opensource.com/sites/default/files/uploads/slide_laplace.png (Figure: Examples of slides created with LaTeX and Beamer)
|
||||
[19]: https://opensource.com/sites/default/files/uploads/slide_faraday.png (Figure: Examples of slides created with LaTeX and Beamer)
|
||||
[20]: https://en.wikipedia.org/wiki/Vector_graphics
|
||||
[21]: https://inkscape.org/
|
||||
[22]: https://en.wikipedia.org/wiki/Raster_graphics
|
||||
[23]: https://www.gimp.org/
|
||||
[24]: https://www.blender.org/
|
||||
[25]: https://www.freecadweb.org/
|
||||
[26]: https://github.com/d3/d3-3.x-api-reference/blob/master/Ordinal-Scales.md#category10
|
||||
[27]: https://d3js.org/
|
||||
[28]: https://matplotlib.org/
|
||||
[29]: https://en.wikipedia.org/wiki/Plot_(graphics)
|
||||
[30]: https://en.wikipedia.org/wiki/Graph_of_a_function
|
||||
[31]: https://opensource.com/sites/default/files/uploads/electromagnetic_wave.png (Figure: Diagram example created with Inkscape by tracing a Blender 3D diagram)
|
||||
[32]: http://www2.pd.infn.it/~fontana/project/teaching/2018/01/02/dice-decay.html
|
||||
[33]: https://en.wikipedia.org/wiki/JavaScript
|
||||
[34]: https://jquery.com/
|
||||
[35]: https://www.mathjax.org/
|
||||
[36]: https://jekyllrb.com/
|
||||
[37]: https://opensource.com/sites/default/files/uploads/dice_decay.png (Figure: Dice modeling radioactive decay, image of the interactive simulation)
|
||||
[38]: https://wiki.gnome.org/Apps/Evince
|
||||
[39]: https://www.gnome.org/gnome-3/
|
||||
[40]: https://okular.kde.org/
|
||||
[41]: https://krita.org/en/
|
||||
[42]: https://docs.krita.org/en/user_manual/python_scripting.html
|
||||
[43]: https://opensource.com/sites/default/files/uploads/krita_screenshot_0.png (Figure: Screenshot of Krita used in class to write notes)
|
||||
[44]: https://qalculate.github.io/
|
||||
[45]: https://wxmaxima-developers.github.io/wxmaxima/
|
||||
[46]: https://opensource.com/sites/default/files/uploads/qalculate_screenshot.png (Figure: Screenshot of Qalculate)
|
||||
[47]: https://obsproject.com/
|
||||
[48]: https://www.openshot.org/
|
||||
[49]: https://opensource.com/sites/default/files/uploads/obs_screenshot.png (Figure: Screenshot of OBS studio recording the screen)
|
||||
[50]: https://getfedora.org/
|
||||
[51]: https://www.centos.org/
|
||||
[52]: https://flatpak.org/
|
||||
[53]: https://www.libreoffice.org/discover/impress/
|
||||
@@ -1,221 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (3 lessons I've learned writing Ansible playbooks)
|
||||
[#]: via: (https://opensource.com/article/20/1/ansible-playbooks-lessons)
|
||||
[#]: author: (Jeff Geerling https://opensource.com/users/geerlingguy)
|
||||
|
||||
3 lessons I've learned writing Ansible playbooks
|
||||
======
|
||||
Avoid common pitfalls and supercharge your Ansible playbook maintenance
|
||||
by following these best practices.
|
||||
![plastic game pieces on a board][1]
|
||||
|
||||
I've used Ansible since 2013 and maintain some of my original playbooks to this day. They have evolved with Ansible from version 1.4 to the current version (as of this writing, 2.9).
|
||||
|
||||
Along the way, as Ansible grew from having dozens to hundreds and now thousands of modules, I've learned a lot about how to make sure my playbooks are maintainable and scalable as my systems grow. Even for simple projects (like the [playbook I use to manage my own laptop][2]), it pays dividends to avoid common pitfalls and make decisions that will make the future you thankful instead of regretful.
|
||||
|
||||
The three main takeaways from this experience are:
|
||||
|
||||
1. Stay organized
|
||||
2. Test early and often
|
||||
3. Simplify, optimize
|
||||
|
||||
|
||||
|
||||
The importance of each lesson I've learned follows in that order, too; it's no use trying to optimize something (point 3) that's already poorly assembled (point 1). Each step builds on the one above, so I'll guide you through each step.
|
||||
|
||||
### Stay organized
|
||||
|
||||
![Organized bins of equipment][3]
|
||||
|
||||
At a bare minimum, you should **store your Ansible playbooks in a Git repository**. This helps with so many things:
|
||||
|
||||
1. Once you have a known working state, you can commit the work (ideally, with tags marking major versions, like 1.0.0 for the first stable version and 2.0.0 for an upgrade or rewrite).
|
||||
2. You can always walk back changes if necessary to a previous known-working state (e.g., by using `git reset` or `git checkout <tag>`).
|
||||
3. Large-scale changes (e.g., feature additions or a major upgrade) can be worked on in a branch, so you can still maintain the existing playbook and have adequate time to work on major changes.
|
||||
|
||||
|
||||
|
||||
Storing playbooks in Git also helps with the second important organization technique: **run your playbooks from a build server**.
|
||||
|
||||
Whether you use [Ansible Tower][4], [Jenkins][5], or some other build system, using a central interface for playbook runs gives you consistency and stability—you don't risk having one admin run a playbook one way (e.g., with the wrong version of roles or an old checkout) and someone else running it another way, breaking your servers.
|
||||
|
||||
It also helps because it forces you to ensure all your playbook's resources are encapsulated in the playbook's repository and build configuration. Ideally, the entire build (including the job configuration) would be captured in the repository (e.g., through the use of a `Jenkinsfile` or its equivalent).
|
||||
|
||||
Another important aspect to organization is **documentation**; at a bare minimum, I have a README in every playbook repository with the following contents:
|
||||
|
||||
* The playbook's purpose
|
||||
* Links to relevant resources (CI build status, external documentation, issue tracking, primary contacts)
|
||||
* Instructions for local testing and development
|
||||
|
||||
|
||||
|
||||
Even if you have the playbook automated through a build server, it is important to have thorough and correct documentation for how to run the playbook otherwise (e.g., locally in a test environment). I like to make sure my projects are easily approachable—not only for others who might eventually need to work with them but also myself! I often forget a nuance or dependency when running a playbook, and the README is the perfect place to outline any peculiarities.
|
||||
|
||||
Finally, the _structure_ of the Ansible tasks themselves are important, and I like to ensure I have a maintainable structure by having **small, readable task files** and by extracting related sets of tasks into **Ansible roles**.
|
||||
|
||||
Generally, if an individual playbook reaches around 100 lines of YAML, I'll start breaking it up into separate task files and using `include_tasks` to include those files. If I find a set of tasks that operates independently and could be broken out into its own [Ansible role][6], I'll work on extracting those tasks and related handlers, variables, and templates.
|
||||
|
||||
Using roles is the best way to supercharge Ansible playbook maintenance; I often have to do similar tasks in many (if not most) playbooks, like managing user accounts or installing and configuring a web server or database. Abstracting these tasks into Ansible roles means I can maintain one set of tasks to be used among many playbooks, with variables to give flexibility where needed.
|
||||
|
||||
Ansible roles can also be contributed back to the community via [Ansible Galaxy][7] if you're able to make them generic and provide the code with an open source license. I have contributed over a hundred roles to Galaxy, and they are made better by the fact that thousands of other playbooks (besides my own) rely on them and break if there is a bug in the role.
|
||||
|
||||
One final note on roles: If you choose to use external roles (either from Galaxy or a private Git repository), I recommend committing the role to your repository (instead of adding it to a `.gitignore` file and downloading the role every time you run your playbook) because I like to avoid relying on downloads from Ansible Galaxy for every playbook run. You should still use a `requirements.yml` file to define role dependencies and define specific versions for the roles so you can choose when to upgrade your dependencies.
|
||||
|
||||
### Test early and often
|
||||
|
||||
![A stack of computer boards][8]
|
||||
|
||||
Ansible allows you to define infrastructure as code. And like any software, it is essential to be able to verify that the code you write does what you expect.
|
||||
|
||||
Like any software, it's best to _test_ your Ansible playbooks. And when I consider testing for any individual Ansible project I build, I think of a spectrum of CI testing options I can use, going in order from the easiest to hardest to implement:
|
||||
|
||||
1. `yamllint`
|
||||
2. `ansible-playbook --syntax-check`
|
||||
3. `ansible-lint`
|
||||
4. [Molecule test][9] (integration tests)
|
||||
5. `ansible-playbook --check` (testing against production)
|
||||
6. Building parallel infrastructure
|
||||
|
||||
|
||||
|
||||
The first three options (linting and running a syntax check on your playbook) are essentially free; they run very fast and can help you avoid the most common problems with your playbook's task structure and formatting.
|
||||
|
||||
They provide some value, but unless the playbook is extremely simple, I like to go beyond basic linting and run tests using [Molecule][9]. I usually use Molecule's built-in Docker integration to run my playbook against a local Docker instance running the same base OS as my production server. For some of my roles, which I run on different Linux distributions (e.g., CentOS and Debian), I run the Molecule test playbook once for each distro—and sometimes with extra test scenarios for more complex roles.
|
||||
|
||||
If you're interested in learning how to test roles with Molecule, I wrote a blog post on the topic a couple of years ago called [Testing your Ansible roles with Molecule][10]. The process for testing full playbooks is similar, and in both cases, the tests can be run inside most CI environments (for example, my [geerlingguy.apache][11] role runs a suite of [Molecule tests via Travis CI][12]).
|
||||
|
||||
The final two test options, running the playbook in `--check` mode or building parallel production infrastructure, require more setup work and often go beyond what's necessary for efficient testing processes. But in cases where playbooks manage servers critical to business revenue, they can be necessary.
|
||||
|
||||
There are a few other things that are important to watch for when running tests and periodically checking or updating your playbooks:
|
||||
|
||||
* Make sure you track (and fix) any `DEPRECATION WARNING`s you see in Ansible's output. Usually, you'll have a year or two before the warning leads to a failure in the latest Ansible version, so the earlier you can update your playbook code, the better.
|
||||
* Every Ansible version has a [porting guide][13]) that is extremely helpful when you're updating from one version to the next.
|
||||
* If you see annoying `WARN` messages in playbook output when you're using a module like `command`, and you know you can safely ignore them, you can add a `warn: no` under the `args` in a task. It's better to squelch these warnings so that more actionable warnings (like deprecation warnings) will be noticed at a glance.
|
||||
|
||||
|
||||
|
||||
Finally, I like to make sure my CI environments are always running the latest Ansible release (and not locked into a specific version that I know works with my playbooks), because I know if a playbook will break right after the new release comes out. My build server is locked into a specific Ansible version, which may be one or two versions behind the latest version, so this gives me the time to ensure I fix any new issues discovered in CI tests before I upgrade my build server to the latest version.
|
||||
|
||||
### Simplify, optimize
|
||||
|
||||
![Charging AirPods][14]
|
||||
|
||||
> "YAML is not a programming language."
|
||||
> — Jeff Geerling
|
||||
|
||||
Simplicity in your playbooks makes maintenance and future changes a lot easier. Sometimes I'll look at a playbook and be puzzled as to what's happening because there are multiple `when` and `until` conditions with a bunch of Python mixed in with Jinja filters.
|
||||
|
||||
If I start to see more than one or two chained filters or Python method calls (especially anything having to do with regular expressions), I see that as a prime candidate for rewriting the required functionality as an Ansible module. The module could be maintained in Python and tested independently and would be easier to maintain as strictly Python code rather than mixing in all the Python inline with your YAML task definitions.
|
||||
|
||||
So my first point is: Stick to Ansible's modules and simple task definitions as much as possible. Try to use Jinja filters wherever possible, and avoid chaining more than one or two filters on a variable at a time. If you have a lot of complex inline Python or Jinja, it's time to consider refactoring it into a custom Ansible module.
|
||||
|
||||
Another common thing I see people do, especially when building out roles the first time, is using complex dict variables where separate "flat" variables may be more flexible.
|
||||
|
||||
For example, instead of having an **apache** role with many options in one giant dict, like this:
|
||||
|
||||
|
||||
```
|
||||
apache:
|
||||
startservers: 2
|
||||
maxclients: 2
|
||||
```
|
||||
|
||||
And consider using separate flat variables:
|
||||
|
||||
|
||||
```
|
||||
apache_startservers: 2
|
||||
apache_maxclients: 2
|
||||
```
|
||||
|
||||
The reason for this is simple: Using flat variables allows playbooks to override one particular value easily, without having to redefine the entire dictionary. This is especially helpful when you have dozens (or in some rare cases, _hundreds_) of default variables in a role.
|
||||
|
||||
Once the playbook and role code looks good, it's time to start thinking about **optimization**.
|
||||
|
||||
A few of the first things I look at are:
|
||||
|
||||
* Can I disable `gather_facts`? Not every playbook needs all the facts, and it adds a bit of overhead on every run, on every server.
|
||||
* Can I increase the number of `forks` Ansible uses? The default is five, but if I have 50 servers, can I operate on 20 or 25 at a time to vastly reduce the amount of time Ansible takes to run a playbook on all the servers?
|
||||
* In CI, can I parallelize test scenarios? Instead of running one test, then the next, if I can start all the tests at once, it will make my CI test cycle much faster. If CI is slow, you'll tend to ignore it or not wait until the test run is complete, so it's important to make sure your test cycle is short.
|
||||
|
||||
|
||||
|
||||
When I'm looking through tasks in a role or playbook, I also look for a few blatant performance issues that are common with certain modules:
|
||||
|
||||
* When using `package` (or `apt`, `yum`, `dnf`, etc.), if there is more than one package being managed, the list should be passed directly to the `name` parameter and not via `with_items` or a `loop`—this way Ansible can efficiently operate on the whole list in one go instead of doing it package by package.
|
||||
* When using `copy`, how many files are being copied? If there is a single file or even a few dozen, it might be fine, but the `copy` module is very slow if you have hundreds or thousands of files to be copied (better to use a module like `synchronize` or a different strategy like copying a tarball and expanding it on the server).
|
||||
* If using `lineinfile` in a loop, it might be more efficient (and sometimes easier to maintain) to use `template` instead and control the entire file in one pass.
|
||||
|
||||
|
||||
|
||||
Once I've gotten most of the low-hanging fruit out of the way, I like to profile my playbook, and Ansible has some built-in tools for this. You can configure extra callback plugins to measure role and task performance by setting the `callback_whitelist` option under `defaults` in your `ansible.cfg`:
|
||||
|
||||
|
||||
```
|
||||
[defaults]
|
||||
callback_whitelist = profile_roles, profile_tasks, timer
|
||||
```
|
||||
|
||||
Now, when you run your playbook, you get a summary of the slowest roles and tasks at the end:
|
||||
|
||||
|
||||
```
|
||||
Monday 10 September 22:31:08 -0500 (0:00:00.851) 0:01:08.824 ******
|
||||
===============================================================================
|
||||
geerlingguy.docker ------------------------------------------------------ 9.65s
|
||||
geerlingguy.security ---------------------------------------------------- 9.33s
|
||||
geerlingguy.nginx ------------------------------------------------------- 6.65s
|
||||
geerlingguy.firewall ---------------------------------------------------- 5.39s
|
||||
geerlingguy.munin-node -------------------------------------------------- 4.51s
|
||||
copy -------------------------------------------------------------------- 4.34s
|
||||
geerlingguy.backup ------------------------------------------------------ 4.14s
|
||||
geerlingguy.htpasswd ---------------------------------------------------- 4.13s
|
||||
geerlingguy.ntp --------------------------------------------------------- 3.94s
|
||||
geerlingguy.swap -------------------------------------------------------- 2.71s
|
||||
template ---------------------------------------------------------------- 2.64s
|
||||
...
|
||||
```
|
||||
|
||||
If anything takes more than a few seconds, it might be good to figure out exactly why it's taking so long.
|
||||
|
||||
### Summary
|
||||
|
||||
I hope you learned a few ways you can make your Ansible Playbooks more maintainable; as I said in the beginning, each of the three takeaways (stay organized, test, then simplify and optimize) builds on the previous, so start by making sure you have clean, documented code, then make sure it's well-tested, and finally look at how you can make it even better and faster!
|
||||
|
||||
* * *
|
||||
|
||||
_This article is a follow up to Jeff's presentation, [Make your Ansible playbooks flexible, maintainable, and scalable][15], at AnsibleFest 2018, which you can [watch here][16]._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/ansible-playbooks-lessons
|
||||
|
||||
作者:[Jeff Geerling][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/geerlingguy
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/team-game-play-inclusive-diversity-collaboration.png?itok=8sUXV7W1 (plastic game pieces on a board)
|
||||
[2]: https://github.com/geerlingguy/mac-dev-playbook
|
||||
[3]: https://opensource.com/sites/default/files/uploads/organized.jpg (Organized bins of equipment)
|
||||
[4]: https://www.ansible.com/products/tower
|
||||
[5]: https://jenkins.io
|
||||
[6]: https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html
|
||||
[7]: https://galaxy.ansible.com
|
||||
[8]: https://opensource.com/sites/default/files/uploads/test-early-often.jpg (A stack of computer boards)
|
||||
[9]: https://molecule.readthedocs.io/en/stable/
|
||||
[10]: https://www.jeffgeerling.com/blog/2018/testing-your-ansible-roles-molecule
|
||||
[11]: https://github.com/geerlingguy/ansible-role-apache
|
||||
[12]: https://travis-ci.org/geerlingguy/ansible-role-apache
|
||||
[13]: https://docs.ansible.com/ansible/latest/porting_guides/porting_guides.html
|
||||
[14]: https://opensource.com/sites/default/files/uploads/simplify-optimize.jpg (Charging AirPods)
|
||||
[15]: https://www.jeffgeerling.com/blog/2019/make-your-ansible-playbooks-flexible-maintainable-and-scalable-ansiblefest-austin-2018
|
||||
[16]: https://www.youtube.com/watch?v=kNDL13MJG6Y
|
||||
@@ -1,52 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (7 open source desktop tools: Download our new eBook)
|
||||
[#]: via: (https://opensource.com/article/20/1/open-source-desktop-tools-guide)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
7 open source desktop tools: Download our new eBook
|
||||
======
|
||||
Choice is more than a feature of Linux; it's a way of life thanks to a
|
||||
wealth of open source tools.
|
||||
![Browser of things][1]
|
||||
|
||||
Linux users say that choice is one of the platform's strengths. On the surface, this might sound self-aggrandizing (or self-deprecating, depending on your perspective). Other operating systems offer choice, too, but once you look at the options available for nearly anything you want to do on Linux, it doesn't take long to conclude that a new word ought to be invented for what we mean by "choice."
|
||||
|
||||
User choice isn't a "feature" of Linux; it's a way of life. Whether you're looking for a whole new desktop or just a new system tray, Linux hackers provide you options. You might also be able to hack some simple commands together to create a batch processor for yourself—and you might publish it online for others, thereby contributing to the array of choice.
|
||||
|
||||
With so many options available, it can be a real challenge to find the solutions you prefer. One of the most effective ways to discover cool new things in the Linux world is through personal recommendation. That's one of the many reasons Opensource.com covers what might seem like random applications—through sharing your experiences with software, others can discover new applications to love without the pain of rummaging through piles of choice.
|
||||
|
||||
### Sharing and open source
|
||||
|
||||
Obviously, you can share software _recommendations_ with friends, whether the software is open source or not. However, in the proprietary world, you can't share the software that you're recommending, and in the world of proprietary software as a service (SaaS), part of the act of sharing is the key component to a pyramid scheme for more user data. It's not quite the same as the no-strings-attached gift of open source.
|
||||
|
||||
Sharing is an integral part of free and open source software. It's one of the [four freedoms][2] defined by the Free Software Foundation, and it's the central concern of [Creative Commons][3].
|
||||
|
||||
While it's easy to fall into the trap of viewing open source sharing as something that applies only to lines of sometimes cryptic-looking code, it goes well beyond that. Sharing is almost endemic to open culture, explicitly allowing and encouraging it on every level, from code, to tutorials and tips, to physical redistribution of a wealth of common goods and services. Part of that is the simple act of telling others about a cool technology that has improved the way we work and live.
|
||||
|
||||
### Download the eBook
|
||||
|
||||
Opensource.com contributor and productivity aficionado Kevin Sonney has shared many of his favorite desktop applications in our latest eBook, [7 open source desktop tools][4]. As is often the case in the open source world, he doesn't just share his knowledge about his favorite desktop tools, he explains how and why he chooses those tools to help you can evaluate them for yourself. Download it today!
|
||||
|
||||
### [Download the 7 open source desktop tools eBook][4]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/open-source-desktop-tools-guide
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/browser_desktop_website_checklist_metrics.png?itok=OKKbl1UR (Browser of things)
|
||||
[2]: https://www.gnu.org/philosophy/free-sw.en.html
|
||||
[3]: https://creativecommons.org
|
||||
[4]: https://opensource.com/downloads/desktop-tools
|
||||
@@ -1,109 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (What Amazon Kindle? Here’s an Open Source eBook Reader)
|
||||
[#]: via: (https://itsfoss.com/open-book/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
What Amazon Kindle? Here’s an Open Source eBook Reader
|
||||
======
|
||||
|
||||
When it comes to an eBook reader, the choices are limited. The market is dominated by [Amazon's proprietary Kindle][1] along with a few other options like Kobo, Nook and Onyx.
|
||||
|
||||
An interesting news for open source enthusiasts is that a developer, [Joey Castillo][2], is working on creating an open source eBook reader appropriately named Open Book.
|
||||
|
||||
### Open Book: An open source eBook reader
|
||||
|
||||
![][3]
|
||||
|
||||
The [Open Book][4] aims to be a simple ‘open’ device that “anyone with a soldering iron can build for themselves”.
|
||||
|
||||
It’s hackable so if you are into DIY stuff and you have some knowledge, you may tweak it to your liking. For example, Joey use [TensorFlow Lite][5] to give voice commands for flipping the pages on Open Book. You can do things like this on your own on this open hardware device.
|
||||
|
||||
> Voice commands on the [#OpenBook][6] with [#TensorFlowLite][7]. When I added a mic amp for voice, I considered this a “someday” feature; I didn’t imagine one could hack it together in an evening! Major credit to [@adafruit][8]; their TFL Arduino port makes this possible. <https://t.co/ix5UK03F3o> [pic.twitter.com/PfXZx99A9y][9]
|
||||
>
|
||||
> — joey castillo (@josecastillo) [December 13, 2019][10]
|
||||
|
||||
If that kind of scares you because you are not really into tinkering with hardware, I have a good news for you. Open Book was named winner of [Hackaday’s Take Flight with Feather contest][11]!
|
||||
|
||||
This means that when the hardware is ready, you should be able to purchase it from [DigiKey][12]. You should be able to fit the device as an eBook reader or experiment with it, if you feel like doing it.
|
||||
|
||||
It kind of reminds me of [Game Shell][13], a single board computer based retro gaming console that could be tinkered into many other things.
|
||||
|
||||
### Open Book specifications
|
||||
|
||||
![][14]
|
||||
|
||||
There are two versions of Open Book: Open Book Feather and E-Book Feather Wing. The eBook wing does less than the Open Book Feather, mainly because it’s limited to using only the pins available via the Feather header.
|
||||
|
||||
You may guess from the name that the project uses [Adafruit’s Feather development boards][15].
|
||||
|
||||
Here are the main specifications for the Open Book (both versions):
|
||||
|
||||
* 4.2 inch, 400 x 300 pixel ePaper display
|
||||
* SAMD51 ARM Cortex-M4 32-bit processor
|
||||
* 7 buttons for navigation (directional pad, select button and page turn buttons)
|
||||
* status LED lights
|
||||
* A microSD card reader
|
||||
* Headphone jack
|
||||
|
||||
|
||||
|
||||
The display seems a bit small, isn’t it?
|
||||
|
||||
### Open Book release, pricing and availability
|
||||
|
||||
![][16]
|
||||
|
||||
Open Book is the winner of [Take Flight with Feather competition by Hackaday][11]. This means that at least 100 Open Book boards will be manufactured and made available for purchase.
|
||||
|
||||
[Liliputing][17] noted that [Adafruit][18] will be handling the manufacturing, and [Digi-Key][19] will eventually be selling Open Book boards.
|
||||
|
||||
At this point, it’s not clear how much will it cost and exactly when it will be available.
|
||||
|
||||
Remember that it’s an open source project. You can find all the circuit designs, source code on its GitHub page and if you have the skills, get the required hardware components and build an Open Book on your own.
|
||||
|
||||
[Open Book on GitHub][4]
|
||||
|
||||
Otherwise, wait for a couple of months (hopefully) for the release of the Open Book boards and then go about experimenting with the device.
|
||||
|
||||
If you like the project and want to support it, you can help [Joey on Pateron][20]. You can follow the updates on the Open Book on the Patreon page, [Joey’s mailing list][21] or Joey’s [Twitter account][2].
|
||||
|
||||
_Do you think the project has potential? Would you buy one when it is available? What do you think of it?_
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/open-book/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/abhishek/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.amazon.com/dp/B07DLPWYB7?tag=chmod7mediate-20&linkCode=ogi&th=1&psc=1 (Amazon's proprietary Kindle)
|
||||
[2]: https://twitter.com/josecastillo
|
||||
[3]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/01/open-book-under-development-feature.jpeg?ssl=1
|
||||
[4]: https://github.com/joeycastillo/The-Open-Book
|
||||
[5]: https://www.tensorflow.org/lite
|
||||
[6]: https://twitter.com/hashtag/OpenBook?src=hash&ref_src=twsrc%5Etfw
|
||||
[7]: https://twitter.com/hashtag/TensorFlowLite?src=hash&ref_src=twsrc%5Etfw
|
||||
[8]: https://twitter.com/adafruit?ref_src=twsrc%5Etfw
|
||||
[9]: https://t.co/PfXZx99A9y
|
||||
[10]: https://twitter.com/josecastillo/status/1205549284403355648?ref_src=twsrc%5Etfw
|
||||
[11]: https://hackaday.io/contest/168107-take-flight-with-feather
|
||||
[12]: https://www.digikey.com/
|
||||
[13]: https://itsfoss.com/gameshell-console/
|
||||
[14]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/01/open-book-board.jpg?ssl=1
|
||||
[15]: https://www.adafruit.com/feather
|
||||
[16]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/01/open-book-demo.jpeg?ssl=1
|
||||
[17]: https://liliputing.com/2020/01/the-open-book-ereader-will-be-a-real-thing-you-can-buy-eventually.html
|
||||
[18]: https://www.adafruit.com/
|
||||
[19]: https://www.digikey.com/en/resources/beta-1
|
||||
[20]: https://www.patreon.com/joeycastillo
|
||||
[21]: http://eepurl.com/gKOpQ9
|
||||
@@ -1,105 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Run your network with open source software)
|
||||
[#]: via: (https://opensource.com/article/20/1/open-source-networking)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
Run your network with open source software
|
||||
======
|
||||
VyOS is an easy-to-install open source router and firewall solution
|
||||
![Multi-colored and directional network computer cables][1]
|
||||
|
||||
Way back in 2005, a company called Vyatta was founded by Allan Leinwand. It offered the first commercially supported, open source router and firewall solution. Named after the ancient Sanskrit for "open," the company's goal of bringing open source networking products to the market was so successful that it was purchased by competitor Brocade. This effectively killed Vyatta, but because Vyatta's product was open source, it didn't stop it. As it turns out, Vyatta's software-defined networking capabilities have been continued and developed as [VyOS][2].
|
||||
|
||||
The VyOS distribution is based on Debian Linux, with source code available from a [Git repository][3] and a [rolling release ISO][4]. For mission-critical applications, there are [long-term support releases and support contracts][5].
|
||||
|
||||
### Building a network
|
||||
|
||||
Networking has changed since 2005, and thanks to the VyOS project, so has the software that drives it. After learning that VyOS was continuing the work of Vyatta, I downloaded the ISO to try it out. The installation process was easier for me than the normal Debian install, which is a simple task. VyOS doesn't boot into a graphical interface, but to a text console. It's just you and a bunch of configuration files, just like you might expect from a serious router.
|
||||
|
||||
The default install of VyOS provides no default configuration. It's entirely up to you to build the network you want to run. Depending on your point of view, that's either refreshing or frustrating, but it gives you an idea of the intended audience: VyOS is built for network engineers who've mapped out their desired topography and are capable of creating it with some subnet calculations and text definitions.
|
||||
|
||||
### The VyOS configure command
|
||||
|
||||
That said, VyOS isn't just a re-branded Debian release. One of its key features is its **configure** command, an interactive tool for defining network definitions that are applied only after you commit them. In addition to feeling familiar to users accustomed to IOS and similar toolchains, this allows you to configure your network even as the router continues its normal operations. Nothing is final until you type **commit**.
|
||||
|
||||
Once you've tested a committed network change, you can use the **save** command to permanently store your configuration files for backup or migration to other VyOS machines.
|
||||
|
||||
You enter configuration mode with the **configure** command. There are too many possible network configurations to go over **configure** or basic networking tasks here, but the syntax is generally intuitive and predictable. For instance, to enable SSH management over port 22212 of your router:
|
||||
|
||||
|
||||
```
|
||||
`# set service ssh port '22212'`
|
||||
```
|
||||
|
||||
To set the network interface card (NIC) **eth1** to an internal IP address and to create a description identifying it as the default gateway:
|
||||
|
||||
|
||||
```
|
||||
# set interfaces ethernet eth1 address '10.1.0.1/24'
|
||||
# set interfaces ethernet eth1 description 'INSIDE'
|
||||
```
|
||||
|
||||
To set the public IP of NIC **eth0** and describe it as the world-facing interface:
|
||||
|
||||
|
||||
```
|
||||
# set interfaces ethernet eth0 address dhcp
|
||||
# set interfaces ethernet eth0 description 'OUTSIDE'
|
||||
```
|
||||
|
||||
To activate network address translation (NAT), it's the same structure:
|
||||
|
||||
|
||||
```
|
||||
set nat source rule 100 outbound-interface 'eth0'
|
||||
set nat source rule 100 source address '10.1.0.0/24'
|
||||
set nat source rule 100 translation address masquerade
|
||||
```
|
||||
|
||||
Assuming those are the only tasks you have for now, commit and save:
|
||||
|
||||
|
||||
```
|
||||
# commit
|
||||
# save && exit
|
||||
Saving configuration to '/config/config.boot'...
|
||||
Done
|
||||
$
|
||||
```
|
||||
|
||||
Although that's probably not everything you need to do in real life, it gives you an idea of the direct simplicity that VyOS provides. You don't have to deal with "reverse engineering" confusing graphical interfaces nor scrubbing through verbose configuration files for a poorly named key. You have a unified interface for every task, whether it's setting up a firewall, implementing DHCP, DNS, quality of service, VPN gateways, IPv6, or whatever else.
|
||||
|
||||
### Professional networking appliance
|
||||
|
||||
VyOS might not be a beginner-level router distribution, but it's a great tool to have on hand even if you're just starting out. There's no better way to learn the basics of networking than to have an open source router available for manipulation and testing. It's the 21st century, after all; you can set up a computer lab consisting of virtual clients networked to one another through a virtual VyOS instance entirely within [GNOME Boxes][6] for $0. You can learn advanced networking concepts and solutions armed with little more than a Linux computer and the excellent [VyOS documentation][7].
|
||||
|
||||
If you're already a networking professional, then take a look at VyOS. You'll find its philosophy on configuration simple and its configure tool efficient.
|
||||
|
||||
Connecting your Linux computer to a network is pretty straightforward, except when it is not. In...
|
||||
|
||||
Jay Turner gives tips for attracting and maintaining an open source community around networking.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/open-source-networking
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/connections_wires_sysadmin_cable.png?itok=d5WqHmnJ (Multi-colored and directional network computer cables)
|
||||
[2]: http://vyos.io
|
||||
[3]: https://github.com/vyos/
|
||||
[4]: https://www.vyos.io/rolling-release/
|
||||
[5]: https://vyos.io/subscriptions/
|
||||
[6]: https://opensource.com/article/19/5/getting-started-gnome-boxes-virtualization
|
||||
[7]: https://vyos.readthedocs.io/en/latest/index.html
|
||||
@@ -1,182 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (5 ways to use Emacs as your RPG dashboard)
|
||||
[#]: via: (https://opensource.com/article/20/1/emacs-rpgs)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
5 ways to use Emacs as your RPG dashboard
|
||||
======
|
||||
Emacs is a flexible way to organize and manage your tabletop
|
||||
role-playing games.
|
||||
![Chess pieces on a chess board][1]
|
||||
|
||||
There are two ways to play a tabletop role-playing game (RPG): You can play an adventure written by the game's publisher or an independent author, or you can play an adventure that is made up as you go. Regardless of which you choose, there's probably prep work to do. One player (generically called the _game master_) must gather monster or enemy stats, loot tables, and references for rules, and the other players must build characters and apportion (pretend) equipment. Nothing's going to eliminate prep work from a complex RPG, but if you're an [Emacs][2] user, you might find that Emacs makes a great dashboard to keep everything all straight.
|
||||
|
||||
### Organize the rules
|
||||
|
||||
Unfortunately, the digital editions of many RPGs are distributed as PDFs because that's what the RPG publisher sent to the printer for the physical edition. PDFs are good at preserving layout, but they're far from an ideal eBook format. If you play RPGs published under an open license, you can often obtain the rules in alternate formats (such as HTML), which gives you more control and flexibility. Even the world's first and most famous RPG, Dungeons & Dragons, provides its rules as a free download in digital format (which has been translated into HTML and Markdown by many a website).
|
||||
|
||||
I open the rules as Markdown in Emacs so that I have a searchable reference at the ready. While opening the rules as a PDF in a PDF reader lets you search for embedded text, using a text file instead provides several benefits. First of all, a text file is much smaller than a PDF, so it's faster to load and to search. Second, text files are easily editable, so if you find a rule that sends you seeking clarification, you can add what you learn (or whatever you make up) directly into your master document. You can also add house rules and additional resources. My aim is to have a single file that contains all of the rules and resources I use in games I run, with everything a quick **Ctrl+s** (**C-s** in Emacs notation) away.
|
||||
|
||||
### Manage initiatives
|
||||
|
||||
Most RPG systems feature a method to determine the order of play during combat. This is commonly called _initiative_, and it comes up a lot since the source of conflict in games often involves combat or some kind of opposed competitive action. It's not that hard to keep track of combat with pencil and paper, but in games where I'm using digital assets anyway, I find it easier to stay digital for everything. Luckily, the venerable [Org mode][3] provides an excellent solution.
|
||||
|
||||
When players roll for initiative, I type their names into Emacs' scratch buffer. Then I type each monster or enemy, along with the hit or health points (HP) of each, followed by two columns of 0:
|
||||
|
||||
|
||||
```
|
||||
brad
|
||||
emily
|
||||
zombie 22 0 0
|
||||
zombie 22 0 0
|
||||
flesh-golem 93 0 0
|
||||
```
|
||||
|
||||
Then I select the block of player characters (PCs) and monsters and use the **org-table-create-or-convert-from-region** function to create an Org mode table around it. Using **Alt+Down arrow** (**M-down** in Emacs notation), I move each PC or monster into the correct initiative order.
|
||||
|
||||
|
||||
```
|
||||
| emily | | | |
|
||||
| flesh-golem | 93 | 0 | 0 |
|
||||
| zombie | 22 | 0 | 0 |
|
||||
| brad | | | |
|
||||
| zombie | 22 | 0 | 0 |
|
||||
```
|
||||
|
||||
During combat, I only need to record damage for monsters, because the players manage their own HP. For the enemies I control in combat, the second column is its HP (its starting number is taken from the RPG system's rules), and the third is the damage dealt during the current round.
|
||||
|
||||
Table formulas in Org mode are defined on a special **TBLFM** line at the end of the table. If you've used any computerized spreadsheet for anything, [Org table][4] will be fairly intuitive. For combat tracking, I want the third column to be subtracted from the second. Columns are indexed from left to right (**$1** for the first, **$2** for the second, and **$3** for the third), so to replace the contents of column $2 with the sum of columns $2 and $3, I add this line to the bottom of the table:
|
||||
|
||||
|
||||
```
|
||||
`#+TBLFM: $2=vsum($2 - $3)`
|
||||
```
|
||||
|
||||
I don't actually type that into Emacs every time the game enters combat mode. Instead, I've defined an auto-completion trigger with Emacs' [abbrev mode][5], a system that allows you to type in a special string of your choosing, which Emacs expands into something more complex. I define my abbreviations in a file called **~/.emacs.d/abbrev_defs**, using **rpgi** followed by a **Space** as the trigger for Emacs to change the line to my initiative table formula:
|
||||
|
||||
|
||||
```
|
||||
(define-abbrev-table 'global-abbrev-table
|
||||
'(
|
||||
("rpgi" "#+TBLFM: $2=vsum($2 - $3)" nil 0)
|
||||
))
|
||||
```
|
||||
|
||||
Each time a player deals damage to a monster, I enter the amount of damage in the damage column. To trigger a table recalculation, I press **Ctrl+u Ctrl+c** (i.e., **C-u C-c** in Emacs) or **Ctrl+c Ctrl+c** (i.e., **C-c C-c**) if I happen to be on the formula line:
|
||||
|
||||
|
||||
```
|
||||
| brad | | |
|
||||
| emily | | |
|
||||
| zombie | 12 | 10 |
|
||||
| zombie | 15 | 7 |
|
||||
| flesh-golem | 91 | 2 |
|
||||
#+TBLFM: $2=vsum($2 - $3)
|
||||
```
|
||||
|
||||
This system isn't perfect. Character names can't contain any spaces because Org table splits cells by white space. It's relatively easy to forget that you processed one line and accidentally reprocess it at the end of a round. To add HP back to a creature's total, you have to use a negative number. (I think of it as negative damage, which suggests health.) Then again, many computerized initiative trackers suffer the same problems, so it's not a particularly bad solution. For me, it's one of the faster methods I've found (I'm happy to admit that [MapTool][6] is the best, but I use my Emacs workflow when I'm not using a digital shared map).
|
||||
|
||||
### View PDFs in DocView
|
||||
|
||||
Sometimes a PDF is unavoidable. Whether it's a d100 list of tavern names or a dungeon map, some resources exist only as a PDF with no extractable text data. In these cases, Emacs' [DocView][7] package can help. DocView is a mode that loads PDF data and generates a PNG file for you to view (Emacs can also view JPEG files). I've found that large PDFs are problematic and slow, but if it's a low-resolution PDF with just one or two pages, DocView is an easy way to reference a document without leaving Emacs.
|
||||
|
||||
I use this mode exclusively for maps, tables, and lists. It's not useful for anything that might involve searching, because text data isn't accessible, but it's an amazingly useful feature for documents you only need to glance at.
|
||||
|
||||
![Emacs for RPG][8]
|
||||
|
||||
The [Ghostscript][9] suite that ships with most Linux distributions (or certainly is available in your repository) allows you to process PDFs, drastically simplifying them by lowering the resolution of images from print quality to screen quality. The command contains mostly PostScript commands and attributes, but you don't need to become a PostScript expert to perform a quick down-res:
|
||||
|
||||
|
||||
```
|
||||
$ gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 \
|
||||
-dPDFSETTINGS=/ebook -dNOPAUSE -dBATCH \
|
||||
-sOutputFile=adventure.pdf \
|
||||
-dDownsampleColorImages=true \
|
||||
-dColorImageResolution=72 big-adventure-module.pdf
|
||||
```
|
||||
|
||||
Opening PDFs in Emacs isn't as exciting as it may sound. It's not by any means a first-class PDF viewer, but for select resources, it can be a convenient way to keep all your information on one screen.
|
||||
|
||||
### Create adventure rap sheets
|
||||
|
||||
Published adventures are often heavy on prose. The theory is that you've paid a lot of money for a prepared adventure, so you obviously want value for your purchase. I do value the lore and world-building that authors put into their adventures, but during a game, I like to have a quick reference to the information I need for the game mechanics to work as intended. In other words, I don't need to have the story of why a trap was placed in a dungeon when a rogue triggers it; I only need to know that the trap exists and what the rogue needs to roll in order to survive.
|
||||
|
||||
I haven't found any modern adventure format that provides me with just that information, so I end up creating my own "rap sheets": a minimal outline for the adventure, with just the game mechanics information I need for each location. Once again, Org mode is the best way for me to keep this information handy.
|
||||
|
||||
In Org mode, you create lists using asterisks for bullet points. For a sub-item, add an asterisk. Even better, press **C-c t** (that's **Ctrl+c** and then the **t** key) to mark the item as a **TODO** item. When your players clear an area in the game, press **C-c t** again to mark the location **DONE**.
|
||||
|
||||
|
||||
```
|
||||
* DONE 1 Entrance
|
||||
** Zombie
|
||||
AC 9 | HP 22
|
||||
* TODO 2 Necromancer's chambers
|
||||
** Flesh golem
|
||||
AC 16 | HP 93
|
||||
** Treasure
|
||||
\- Gold ring (200 gp)
|
||||
\- Rusty nail (1 cp)
|
||||
Cursed (roll on curse table)
|
||||
** Secret door (DC 20)
|
||||
\- to area 11
|
||||
```
|
||||
|
||||
Each asterisk is collapsible, so you can get a summary of a global area by collapsing your list down to just the top-level:
|
||||
|
||||
|
||||
```
|
||||
* DONE 1 Entrance
|
||||
* TODO 2 Necromancer's chambers
|
||||
* TODO 3 Wyrmling nursery
|
||||
* TODO 4 Caretaker's chambers
|
||||
* TODO 5 Caretaker's laboratory
|
||||
```
|
||||
|
||||
An added bonus: I find that making my own rap sheets helps me internalize both the mechanics and the lore of the adventure I'm preparing, so the benefits to this method are numerous. Since I manage any adventure I run in Emacs with Git, once I do the prep work for an adventure, I have fresh copies of all my assets in case I run the adventure with another group or with a set of fresh characters.
|
||||
|
||||
### Make your own adventure journal
|
||||
|
||||
Generally, I let my players keep their own notes about the adventure because I want to encourage players to interpret the events happening in the adventure for themselves. However, a game master needs private notes to keep all of the improvised data in order. For example, if a published adventure doesn't feature a blacksmith shop, but players decide to visit a blacksmith, then a blacksmith needs to be invented in the moment. If the players revisit the blacksmith six weeks later, then they expect it to be the same blacksmith, and it's up to the game master to keep track of such additions to the published setting. I manage my personal notes about adventures in two different ways, depending on what's available to me.
|
||||
|
||||
If I have the text of the adventure in an editable format (such as HTML or Markdown), I enter my additions into the adventure as if the publisher had included them from the start. This means there's always one source of truth for the setting and for significant events.
|
||||
|
||||
If I haven't been able to get an editable copy of the adventure because it's a hard copy or a PDF that's not easily modified, then I write my additions into my rap sheets in Org mode. This functionally means that there's still one source of truth because my rap sheets are the first place I look for information, falling back on the published text only for details I've forgotten. Sometimes I like my additions enough to merge them back into my Git master for the adventure, but usually, I trust in improvisation and let additions happen dynamically for each group that plays the adventure.
|
||||
|
||||
### Why Emacs is my favorite RPG dashboard
|
||||
|
||||
I've fallen into using Emacs for RPGs because it serves as the heads-up display of my dreams. The "right" answer is probably a good [tiling window manager][10], but until I implement that, I'm happy with Emacs. Everything's bound to keyboard shortcuts designed for specificity and speed, and there's just enough easy customization that I can hack together good-enough solutions—sometimes even while players are arguing with one another about what to do next.
|
||||
|
||||
I've tried juggling multiple desktops, several PDF reader windows, and a spreadsheet for initiatives; while it's a fine experience, nothing has equaled the fluidity of Emacs as my RPG dashboard.
|
||||
|
||||
* * *
|
||||
|
||||
Hey! do you love Emacs? [Write an article][11] about how you use an Emacs (GNU or otherwise) for inclusion in our forthcoming Emacs series!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/emacs-rpgs
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/life-chess-games.png?itok=U1lWMZ0y (Chess pieces on a chess board)
|
||||
[2]: https://opensource.com/life/16/2/intro-to-emacs
|
||||
[3]: https://orgmode.org/
|
||||
[4]: https://orgmode.org/manual/Tables.html
|
||||
[5]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Abbrevs.html#Abbrevs
|
||||
[6]: https://opensource.com/article/19/6/how-use-maptools
|
||||
[7]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Document-View.html
|
||||
[8]: https://opensource.com/sites/default/files/uploads/emacs-rpg.jpg (Emacs for RPG)
|
||||
[9]: https://www.ghostscript.com/
|
||||
[10]: https://opensource.com/article/19/12/ratpoison-linux-desktop
|
||||
[11]: https://opensource.com/how-submit-article
|
||||
@@ -1,77 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (ProtonVPN adopts GPLv3, Mozilla Thunderbird gets new home, and more news)
|
||||
[#]: via: (https://opensource.com/article/20/2/news-february-1)
|
||||
[#]: author: (Scott Nesbitt https://opensource.com/users/scottnesbitt)
|
||||
|
||||
ProtonVPN adopts GPLv3, Mozilla Thunderbird gets new home, and more news
|
||||
======
|
||||
Catch up on the biggest open source headlines from the past two weeks.
|
||||
![][1]
|
||||
|
||||
In this edition of our open source news roundup, we take a look ProtonVPN apps going open, Microsoft's code analysis tool, Mozilla Thunderbird's new home, and more!
|
||||
|
||||
### ProtonVPN apps go open source
|
||||
|
||||
People wanting to use the internet securely and privately do the deed using a Virtual Private Network (VPN). But which VPNs can you really trust? The company behind the popular ProtonVPN service made a big move to gain that trust by [releasing the source code][2] for all its apps.
|
||||
|
||||
By making its apps open source, ProtonVPN is giving security experts the chance to "inspect its encryption implementations and how the company handles user data, giving users confidence that the company is adhering to its strict privacy policy." According to an [article at TechRadar][3], ProtonVPN also engaged "security firm SEC Consult on a full security audit that was able to verify the security of the company's software."
|
||||
|
||||
You can find the source code for the apps [on GitHub][4] and links to the audit reports [in this blog post][5].
|
||||
|
||||
### Mozilla Thunderbird gets a new home
|
||||
|
||||
What a difference a few years makes. When the Mozilla Foundation announced in 2015 that it was considering spinning off the Thunderbird email client, the software's adherents feared the worst. Since then, Thunderbird has persisted but its fate has also been up in the air. [That's changed][6] with the formation of MZLA Technologies Corporation.
|
||||
|
||||
MZLA Technologies is "a new wholly owned subsidiary of the Mozilla Foundation" that's the new home of the Thunderbird project. The move to the new corporation means that development will continue on the software and that move "won’t have an impact on Thunderbird’s day-to-day running." According to Thunderbird's Phillip Kewisch, shifting the project to MZLA Technologies enables it to "explore offering our users products and services that were not possible under the Mozilla Foundation."
|
||||
|
||||
### Encrypting the Internet of Things
|
||||
|
||||
The so-called Internet of Things (IoT) has promised so much. That promise has been lost under the weight of the often paper-thin security of IoT devices. Teserakt, a Swiss security firm, is trying turn that around with the [release of E4][7], a "cryptographic implant that IoT manufacturers can integrate into their servers."
|
||||
|
||||
Teserakt's CEO Jean-Philippe Aumasson said that E4 came about because there are "so many machines and entities that do not have the need to view or modify (data from IoT devices), so they shouldn’t have access to it." E4 provides end-to-end encryption of data which bolsters "defenses for information in transit and offers protection against data interception and manipulation."
|
||||
|
||||
If you're interesting taking a peek at E4, you can do that in [Teserakt's GitHub repository][8].
|
||||
|
||||
#### In other news
|
||||
|
||||
* [Scientists working with Google just published the most detailed brain scans ever created][9]
|
||||
* [AWS JPL open source rover challenge open to competitors][10]
|
||||
* [Intel joins CHIPS Alliance, contributes advanced interface bus][11]
|
||||
* [Why UK leaders need open technology for the disrupted future][12]
|
||||
* [Microsoft open sources code analysis tool][13]
|
||||
|
||||
|
||||
|
||||
_Thanks, as always, to Opensource.com staff members and moderators for their help this week. Make sure to check out [our event calendar][14], to see what's happening next week in open source._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/2/news-february-1
|
||||
|
||||
作者:[Scott Nesbitt][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/scottnesbitt
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/weekly_news_roundup_tv.png?itok=tibLvjBd
|
||||
[2]: https://betanews.com/2020/01/21/protonvpn-open-source/
|
||||
[3]: https://www.techradar.com/au/news/protonvpn-releases-source-code-and-undergoes-security-audit
|
||||
[4]: https://github.com/ProtonVPN
|
||||
[5]: https://protonvpn.com/blog/open-source/
|
||||
[6]: https://blog.thunderbird.net/2020/01/thunderbirds-new-home/
|
||||
[7]: https://www.wired.com/story/e4-iot-encryption/
|
||||
[8]: https://github.com/teserakt-io/
|
||||
[9]: https://thenextweb.com/artificial-intelligence/2020/01/22/scientists-working-with-google-just-published-the-most-detailed-brain-scans-ever-created/
|
||||
[10]: https://www.therobotreport.com/aws-jpl-open-source-rover-challenge-open-to-competitors/
|
||||
[11]: https://chipsalliance.org/announcement/2020/01/22/intel-joins-chips-alliance-to-promote-advanced-interface-bus-aib-as-an-open-standard/
|
||||
[12]: https://www.information-age.com/leaders-open-technology-disrupted-future-123487150/
|
||||
[13]: https://www.infoworld.com/article/3516147/microsoft-releases-open-source-source-code-analyzer.html
|
||||
[14]: https://opensource.com/resources/conferences-and-events-monthly
|
||||
@@ -1,72 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (New zine: Become a SELECT Star!)
|
||||
[#]: via: (https://jvns.ca/blog/2020/02/03/new-zine--become-a-select-star/)
|
||||
[#]: author: (Julia Evans https://jvns.ca/)
|
||||
|
||||
New zine: Become a SELECT Star!
|
||||
======
|
||||
|
||||
On Friday I published a zine about SQL called “Become a SELECT Star!”
|
||||
|
||||
You can get it for $12 at <https://wizardzines.com/zines/sql>. If you buy it, you’ll get a PDF that you can either read on your computer or print out. You can also get a pack of [all 7 zines][1] so far.
|
||||
|
||||
Here’s the cover and table of contents:
|
||||
|
||||
[![][2]][3] <https://jvns.ca/images/sql-toc.png>
|
||||
|
||||
### why SQL?
|
||||
|
||||
I got excited about writing a zine about SQL because at my old job I wrote a ton of SQL queries (mostly related to machine learning) and by doing that I learned there are a lot of weird things about SQL! For example – [SQL queries don’t actually start with SELECT][4]. And the way [NULL behaves isn’t really intuitive at first][5].
|
||||
|
||||
It’s been really fun to go back and try to explain the basics of SQL from the beginning. (what’s the difference between WHERE and HAVING? what’s the basic idea with indexes actually? how do you write a join?)
|
||||
|
||||
I think SQL is a really nice thing to know because there are SO MANY SQL databases out there, and some of them are super powerful! (like BigQuery and Redshift). So if you know SQL and have access to one of these big data warehouses you can write queries that crunch like 10 billion rows of data really quickly.
|
||||
|
||||
### lots of examples
|
||||
|
||||
I ended up spending a lot of time on the examples in this zine, more than in any previous zine. My friend [Anton][6] helped me come up with a fun way to illustrate them, where you can clearly see the query, the table it’s running on, and what the query outputs. Like this:
|
||||
|
||||
![][7]
|
||||
|
||||
### experiment: include a SQL playground
|
||||
|
||||
All the examples in the zine are real queries that you can run. So I thought: why not provide a simple environment where people can actually run those queries (and variations on those queries) to try things out?
|
||||
|
||||
So I built a small [playground where you can run queries on the example tables in the zine][8]. It uses SQLite compiled to web assembly, so all the queries run in your browser. It wasn’t too complicated to build – I just used my minimal Javascript/CSS skills and vue.js.
|
||||
|
||||
I’d love to hear any feedback about whether this is helpful or not – the example tables in the zine are really small (you can only print out small SQL tables!), so the biggest table in the example set has 9 rows or something.
|
||||
|
||||
### what’s next: probably containers
|
||||
|
||||
I think that next up is going to be a zine on containers, which is more of a normal systems-y topic for me. (for example: [namespaces][9], [cgroups][10], [why containers?][11])
|
||||
|
||||
Here’s a link to where to [get the zine][3] again :)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://jvns.ca/blog/2020/02/03/new-zine--become-a-select-star/
|
||||
|
||||
作者:[Julia Evans][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://jvns.ca/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://wizardzines.com/zines/all-the-zines/
|
||||
[2]: https://jvns.ca/images/sql-cover.png
|
||||
[3]: https://wizardzines.com/zines/sql
|
||||
[4]: https://jvns.ca/blog/2019/10/03/sql-queries-don-t-start-with-select/
|
||||
[5]: https://twitter.com/b0rk/status/1195184321818243083
|
||||
[6]: http://www.cat-bus.com/
|
||||
[7]: https://jvns.ca/images/sql-diagram.png
|
||||
[8]: https://sql-playground.wizardzines.com
|
||||
[9]: https://twitter.com/b0rk/status/1195725346970181632
|
||||
[10]: https://twitter.com/b0rk/status/1214341831049252870
|
||||
[11]: https://twitter.com/b0rk/status/1224500774450929664
|
||||
@@ -1,430 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Ansible Roles Quick Start Guide with Examples)
|
||||
[#]: via: (https://www.2daygeek.com/ansible-roles-quick-start-guide-with-examples/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
Ansible Roles Quick Start Guide with Examples
|
||||
======
|
||||
|
||||
Ansible is an excellent configuration management and orchestration tool.
|
||||
|
||||
It is designed to easily automate the entire infrastructure.
|
||||
|
||||
We have written three articles in the past about Ansible.
|
||||
|
||||
If you are new to Ansible, I advise you to read the articles below, which will help you understand the basics of Ansible.
|
||||
|
||||
* **Part-1: [Ansible Automation Tool Installation, Configuration and Quick Start Guide][1]**
|
||||
* **Part-2: [Ansible Ad-hoc Command Quick Start Guide with Examples][2]**
|
||||
* **Part-3: [Ansible Playbooks Quick Start Guide with Examples][3]**
|
||||
|
||||
|
||||
|
||||
### What’s Ansible Roles?
|
||||
|
||||
Ansible Roles provides the framework for automatically loading certain tasks, files, vars, templates, and handlers from a known file structure into the playbook.
|
||||
|
||||
The primary mechanism of role is to break a playbook into multiple pieces (files).
|
||||
|
||||
This makes it easier for you to write complex playbooks and makes them easier to reuse.
|
||||
|
||||
Also, it reduces the syntax error by breaking it into multiple files.
|
||||
|
||||
Ansible Playbook is a set of roles, and each role essentially performs a specific function.
|
||||
|
||||
The Ansible roles are reusable (you can import the roles into other paybooks as well) because the roles are independent of each other and do not depend on others while executing.
|
||||
|
||||
Ansible offers a two-example directory structure that helps you organize your ansible playbook content, and its use.
|
||||
|
||||
It is not limited to using the same data structure, and you can create your own directory structure based on your needs.
|
||||
|
||||
Each directory is have a **“main.yml”** file, which contains the basic content:
|
||||
|
||||
### Ansible Roles Default Directory Structure
|
||||
|
||||
Ansible Best Practices provides the following two directory structures. The first is very simple and well suited for a small environment with simple production and inventory files.
|
||||
|
||||
```
|
||||
production # inventory file for production servers
|
||||
staging # inventory file for staging environment
|
||||
|
||||
group_vars/
|
||||
group1.yml # here we assign variables to particular groups
|
||||
group2.yml
|
||||
host_vars/
|
||||
hostname1.yml # here we assign variables to particular systems
|
||||
hostname2.yml
|
||||
|
||||
library/ # if any custom modules, put them here (optional)
|
||||
module_utils/ # if any custom module_utils to support modules, put them here (optional)
|
||||
filter_plugins/ # if any custom filter plugins, put them here (optional)
|
||||
|
||||
site.yml # master playbook
|
||||
webservers.yml # playbook for webserver tier
|
||||
dbservers.yml # playbook for dbserver tier
|
||||
|
||||
roles/
|
||||
common/ # this hierarchy represents a "role"
|
||||
tasks/ #
|
||||
main.yml # <-- tasks file can include smaller files if warranted
|
||||
handlers/ #
|
||||
main.yml # <-- handlers file
|
||||
templates/ # <-- files for use with the template resource
|
||||
ntp.conf.j2 # <------- templates end in .j2
|
||||
files/ #
|
||||
bar.txt # <-- files for use with the copy resource
|
||||
foo.sh # <-- script files for use with the script resource
|
||||
vars/ #
|
||||
main.yml # <-- variables associated with this role
|
||||
defaults/ #
|
||||
main.yml # <-- default lower priority variables for this role
|
||||
meta/ #
|
||||
main.yml # <-- role dependencies
|
||||
library/ # roles can also include custom modules
|
||||
module_utils/ # roles can also include custom module_utils
|
||||
lookup_plugins/ # or other types of plugins, like lookup in this case
|
||||
|
||||
webtier/ # same kind of structure as "common" was above, done for the webtier role
|
||||
monitoring/ # ""
|
||||
fooapp/ # ""
|
||||
```
|
||||
|
||||
If you want to use this directory structure run the command below.
|
||||
|
||||
```
|
||||
$ sudo mkdir -p group_vars host_vars library module_utils filter_plugins
|
||||
$ sudo mkdir -p roles/common/{tasks,handlers,templates,files,vars,defaults,meta,library,module_utils,lookup_plugins}
|
||||
$ sudo touch production staging site.yml roles/common/{tasks,handlers,templates,files,vars,defaults,meta}/main.yml
|
||||
```
|
||||
|
||||
The second one is appropriate when you have a very complex inventory environment.
|
||||
|
||||
```
|
||||
inventories/
|
||||
production/
|
||||
hosts # inventory file for production servers
|
||||
group_vars/
|
||||
group1.yml # here we assign variables to particular groups
|
||||
group2.yml
|
||||
host_vars/
|
||||
hostname1.yml # here we assign variables to particular systems
|
||||
hostname2.yml
|
||||
staging/
|
||||
hosts # inventory file for staging environment
|
||||
group_vars/
|
||||
group1.yml # here we assign variables to particular groups
|
||||
group2.yml
|
||||
host_vars/
|
||||
stagehost1.yml # here we assign variables to particular systems
|
||||
stagehost2.yml
|
||||
library/
|
||||
module_utils/
|
||||
filter_plugins/
|
||||
site.yml
|
||||
webservers.yml
|
||||
dbservers.yml
|
||||
roles/
|
||||
common/
|
||||
webtier/
|
||||
monitoring/
|
||||
fooapp/
|
||||
```
|
||||
|
||||
If you want to use this directory structure run the command below.
|
||||
|
||||
```
|
||||
$ sudo mkdir -p inventories/{production,staging}/{group_vars,host_vars}
|
||||
$ sudo touch inventories/{production,staging}/hosts
|
||||
$ sudo mkdir -p group_vars host_vars library module_utils filter_plugins
|
||||
$ sudo mkdir -p roles/common/{tasks,handlers,templates,files,vars,defaults,meta,library,module_utils,lookup_plugins}
|
||||
$ sudo touch site.yml roles/common/{tasks,handlers,templates,files,vars,defaults,meta}/main.yml
|
||||
```
|
||||
|
||||
### How to Create a Simple Ansible Roles Directory Structure
|
||||
|
||||
By default there is no “Roles” directory in your Ansible directory, so you have to create it first.
|
||||
|
||||
```
|
||||
$ sudo mkdir /etc/ansible/roles
|
||||
```
|
||||
|
||||
Use the following Ansible Galaxy command to create a simple directory structure for a role.
|
||||
|
||||
```
|
||||
$ sudo ansible-galaxy init [/Path/to/Role_Name]
|
||||
```
|
||||
|
||||
### What’s Ansible Galaxy?
|
||||
|
||||
Ansible Galaxy refers to the Galaxy Website, a free platform for finding, downloading and sharing community developed roles.
|
||||
|
||||
The Galaxy website offers pre-packaged units such as roles and collections. Provisioning infrastructure, deploy applications and you’ll find plenty of roles for all the tasks that you do on a daily basis.
|
||||
|
||||
While writing this article I saw **23478** results and it is growing on a daily basis.
|
||||
|
||||
To prove this, we are going to create the **“webserver”** role. To do so, run the following command.
|
||||
|
||||
```
|
||||
$ sudo ansible-galaxy init /etc/ansible/roles/webserver
|
||||
- Role /etc/ansible/roles/webserver was created successfully
|
||||
```
|
||||
|
||||
Once you have created a new role, use the tree commmand to view the detailed directory structure.
|
||||
|
||||
```
|
||||
$ tree /etc/ansible/roles/webserver
|
||||
|
||||
/etc/ansible/roles/webserver
|
||||
├── defaults
|
||||
│ └── main.yml
|
||||
├── files
|
||||
├── handlers
|
||||
│ └── main.yml
|
||||
├── meta
|
||||
│ └── main.yml
|
||||
├── README.md
|
||||
├── tasks
|
||||
│ └── main.yml
|
||||
├── templates
|
||||
├── tests
|
||||
│ ├── inventory
|
||||
│ └── test.yml
|
||||
└── vars
|
||||
└── main.yml
|
||||
8 directories, 8 files
|
||||
```
|
||||
|
||||
It comes with 8 directories and 8 files, details are as follows.
|
||||
|
||||
* **defaults:** Default variables for the role
|
||||
* **handlers:** It contains handlers, which may be used by this role or even anywhere outside this role.
|
||||
* **meta:** Defines some meta data for this role.
|
||||
* **tasks:** It contains the main list of tasks to be executed by the role.
|
||||
* **templates:** It contains templates which can be deployed via this role.
|
||||
* **vars:** Other variables for the role.
|
||||
|
||||
|
||||
|
||||
This is a sample playbook that sets up the Apache Web server on Debian and Red Hat-based systems.
|
||||
|
||||
```
|
||||
$ sudo nano /etc/ansible/playbooks/webserver.yml
|
||||
|
||||
---
|
||||
- hosts: web
|
||||
become: yes
|
||||
name: "Install and Configure Apache Web Server on Linux"
|
||||
tasks:
|
||||
- name: "Install Apache Web Server on RHEL Based Systems"
|
||||
yum: name=httpd update_cache=yes state=latest
|
||||
when: ansible_facts['os_family']|lower == "redhat"
|
||||
|
||||
- name: "Install Apache Web Server on Debian Based Systems"
|
||||
apt: name=apache2 update_cache=yes state=latest
|
||||
when: ansible_facts['os_family']|lower == "debian"
|
||||
|
||||
- name: "Start the Apache Web Server"
|
||||
service:
|
||||
name: httpd
|
||||
state: started
|
||||
enabled: yes
|
||||
|
||||
- name: "Enable mod_rewrite module"
|
||||
apache2_module:
|
||||
name: rewrite
|
||||
state: present
|
||||
|
||||
notify:
|
||||
- restart apache
|
||||
|
||||
handlers:
|
||||
- name: "Restart Apache2 Web Server"
|
||||
service:
|
||||
name: apache2
|
||||
state: restarted
|
||||
|
||||
- name: "Restart httpd Web Server"
|
||||
service:
|
||||
name: httpd
|
||||
state: restarted
|
||||
```
|
||||
|
||||
Let’s break the playbook above into Ansible roles. If you only have simple contents, add them to the **“main.yml”** file, otherwise create separate **“xyz.yml”** files for each task.
|
||||
|
||||
**Make a note:** **“notify”** should be included in the last task, which is why we have added it to the **“module.yml”** file.
|
||||
|
||||
Create a separate task to install the Apache Web Server on Red Hat-based systems.
|
||||
|
||||
```
|
||||
$ sudo vi /etc/ansible/roles/webserver/tasks/redhat.yml
|
||||
|
||||
---
|
||||
- name: "Install Apache Web Server on RHEL Based Systems"
|
||||
yum:
|
||||
name: httpd
|
||||
update_cache: yes
|
||||
state: latest
|
||||
```
|
||||
|
||||
Create a separate task to install the Apache Web Server on Debian-based systems.
|
||||
|
||||
```
|
||||
$ sudo vi /etc/ansible/roles/webserver/tasks/debian.yml
|
||||
|
||||
---
|
||||
- name: "Install Apache Web Server on Debian Based Systems"
|
||||
apt:
|
||||
name: apache2
|
||||
update_cache: yes
|
||||
state: latest
|
||||
```
|
||||
|
||||
Create a separate task to start the Apache web server on Red Hat based systems.
|
||||
|
||||
```
|
||||
$ sudo vi /etc/ansible/roles/webserver/tasks/service-httpd.yml
|
||||
|
||||
---
|
||||
- name: "Start the Apache Web Server"
|
||||
service:
|
||||
name: httpd
|
||||
state: started
|
||||
enabled: yes
|
||||
```
|
||||
|
||||
Create a separate task to start the Apache web server on Debian based systems.
|
||||
|
||||
```
|
||||
$ sudo vi /etc/ansible/roles/webserver/tasks/service-apache2.yml
|
||||
|
||||
---
|
||||
- name: "Start the Apache Web Server"
|
||||
service:
|
||||
name: apache2
|
||||
state: started
|
||||
enabled: yes
|
||||
```
|
||||
|
||||
Create a separate task to copy the index file into the Apache web root directory.
|
||||
|
||||
```
|
||||
$ sudo nano /etc/ansible/roles/webserver/tasks/configure.yml
|
||||
|
||||
---
|
||||
- name: Copy index.html file
|
||||
copy: src=files/index.html dest=/var/www/html
|
||||
```
|
||||
|
||||
Create a separate task to install rewrite module on Debian based systems.
|
||||
|
||||
```
|
||||
$ sudo nano /etc/ansible/roles/webserver/tasks/modules.yml
|
||||
|
||||
---
|
||||
- name: "Enable mod_rewrite module"
|
||||
apache2_module:
|
||||
name: rewrite
|
||||
state: present
|
||||
|
||||
notify:
|
||||
- restart apache
|
||||
```
|
||||
|
||||
Finally import all tasks into the **“main.yml”** file of the Tasks directory.
|
||||
|
||||
```
|
||||
$ sudo nano /etc/ansible/roles/webserver/tasks/main.yml
|
||||
|
||||
---
|
||||
tasks file for /etc/ansible/roles/webserver
|
||||
- import_tasks: redhat.yml
|
||||
when: ansible_facts['os_family']|lower == 'redhat'
|
||||
|
||||
- import_tasks: debian.yml
|
||||
when: ansible_facts['os_family']|lower == 'debian'
|
||||
|
||||
- import_tasks: service-httpd.yml
|
||||
- import_tasks: service-apache2.yml
|
||||
- import_tasks: configure.yml
|
||||
- import_tasks: modules.yml
|
||||
when: ansible_facts['os_family']|lower == 'debian'
|
||||
```
|
||||
|
||||
Add the handler information to the **“main.yml”** file of the handlers directory.
|
||||
|
||||
```
|
||||
$ sudo nano /etc/ansible/roles/webserver/handlers/main.yml
|
||||
|
||||
---
|
||||
#handlers file for /etc/ansible/roles/webserver
|
||||
- name: "Restart httpd Web Server"
|
||||
service:
|
||||
name: httpd
|
||||
state: restarted
|
||||
|
||||
- name: "Restart Apache2 Web Server"
|
||||
service:
|
||||
name: apache2
|
||||
state: restarted
|
||||
```
|
||||
|
||||
Add an index.html file to the files directory. This is the file you want to copy on the target server.
|
||||
|
||||
```
|
||||
$ sudo nano /etc/ansible/roles/webserver/files/index.html
|
||||
|
||||
This is the test page of 2DayGeek.com for Ansible Tutorials
|
||||
```
|
||||
|
||||
You have successfully broken the playbook into Ansible roles using the steps above. Now, your new Ansible role may look like the one below.
|
||||
|
||||
![][4]
|
||||
|
||||
If you have done everything for your Ansible role, then finally import this role into your playbook.
|
||||
|
||||
```
|
||||
$ sudo nano /etc/ansible/playbooks/webserver-role.yml
|
||||
|
||||
---
|
||||
- hosts: all
|
||||
become: yes
|
||||
name: "Install and Configure Apache Web Server on Linux"
|
||||
roles:
|
||||
- webserver
|
||||
```
|
||||
|
||||
Once you have done everything, I advise you to check the Playbook syntax before executing it.
|
||||
|
||||
```
|
||||
$ ansible-playbook /etc/ansible/playbooks/webserver-role.yml --syntax-check
|
||||
|
||||
playbook: /etc/ansible/playbooks/webserver-role.yml
|
||||
```
|
||||
|
||||
Finally execute the Ansible playbook to see the magic.
|
||||
|
||||
![][4]
|
||||
|
||||
Hope this tutorial helped you to learn about the Ansible roles. If you are satisfied, please share the article on social media. If you would like to improve this article, add your comments in the comment section.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/ansible-roles-quick-start-guide-with-examples/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/install-configure-ansible-automation-tool-linux-quick-start-guide/
|
||||
[2]: https://www.2daygeek.com/ansible-ad-hoc-command-quick-start-guide-with-examples/
|
||||
[3]: https://www.2daygeek.com/ansible-playbooks-quick-start-guide-with-examples/
|
||||
[4]: data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
@@ -1,158 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (SimpleLogin: Open Source Solution to Protect Your Email Inbox From Spammers)
|
||||
[#]: via: (https://itsfoss.com/simplelogin/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
SimpleLogin: Open Source Solution to Protect Your Email Inbox From Spammers
|
||||
======
|
||||
|
||||
_**Brief: SimpleLogin is an open-source service to help you protect your email address by giving you a permanent alias email address.**_
|
||||
|
||||
Normally, you have to use your real email address to sign up for services that you want to use personally or for your business.
|
||||
|
||||
In the process, you’re sharing your email address – right? And, that potentially exposes your email address to spammers (depending on where you shared the information).
|
||||
|
||||
What if you can protect your real email address by providing an alias for it instead? No – I’m not talking about disposable email addresses like 10minutemail which could be useful for temporary sign-ups – even though they’ve been blocked by certain services.
|
||||
|
||||
I’m talking about something similar to “_[Hide My Emai for Sign in with Apple ID][1]_” but a free and open-source solution i.e [SimpleLogin][2].
|
||||
|
||||
### SimpleLogin: An open source service to protect your email inbox
|
||||
|
||||
![][3]
|
||||
|
||||
_It is worth noting that you still have to use your existing email client (or email service) to receive and send emails – but with this service, you get to hide your real email ID._
|
||||
|
||||
SimpleLogin is an open-source project (you can find it on [GitHub][4]) available for free (with premium upgrade options) that aims to keep your email private.
|
||||
|
||||
Unlike temporary email services, it generates a permanent random alias for your email address that you can use to sign up for services without revealing your real email.
|
||||
|
||||
The alias works as a point of contact to forward the emails intended to your real email ID.
|
||||
|
||||
**You’ll receive the emails sent to the alias email address in your real email inbox and if you believe that the alias is receiving too many spams, you block the alias. This way, you completely stop getting spam emails sent to the particular aliased email address.**
|
||||
|
||||
Not just limited to receiving emails but you can also send emails through the alias email address. Interesting, right? And, using this coupled with [secure email services][5] should be a good combination to protect your privacy.
|
||||
|
||||
**Recommended Read:**
|
||||
|
||||
![][6]
|
||||
|
||||
#### [Best VPN Services for Privacy Minded Linux Users][7]
|
||||
|
||||
Here are our recommendations for best VPN services for Linux users to secure their privacy and enhance their online security. Check it out.
|
||||
|
||||
### Features of SimpleLogin
|
||||
|
||||
![][8]
|
||||
|
||||
Before taking a look at how it works, let me highlight what it offers overall to the Internet users and web developers as well:
|
||||
|
||||
* Protects your real email address by generating an alias address
|
||||
* Send/Recieve emails through your alias
|
||||
* Block the alias if emails get too spammy
|
||||
* Custom domain supported with premium plans
|
||||
* You can choose to self-host it
|
||||
* If you’re a web developer, you can follow the [documentation][9] to integrate a “**Sign in with SimpleLogin**” button to your login page.
|
||||
|
||||
|
||||
|
||||
You can either utilize the web browser or use the extension for Firefox, Chrome and Safari.
|
||||
|
||||
[SimpleLogin][2]
|
||||
|
||||
### How SimpleLogin Works?
|
||||
|
||||
![][10]
|
||||
|
||||
To start with, you’ll have to sign up for the service with your primary email ID that you want to keep private.
|
||||
|
||||
Once done – you have to use your alias email to sign up for any other services you want.
|
||||
|
||||
![][11]
|
||||
|
||||
The number of aliases generated is limited in the free plan – however, you can upgrade to the premium plan if you want to generate different alias email addresses for every site.
|
||||
|
||||
You don’t necessarily need to use the web portal, you can use the browser extension to generate aliases and use them when needed as shown in the image below:
|
||||
|
||||
![][12]
|
||||
|
||||
Even if you want to send an email without revealing your real email ID, just generate an alias email by typing in the receiver’s email ID and paste the alias in your email client to send it.
|
||||
|
||||
### Brief conversation with SimpleLogin’s founder
|
||||
|
||||
I was quite impressed to see an open-source service like this – so I reached out to [**Son Nguyen Kim**][13] (_SimpleLogin’s founder_). Here’s a few things I asked along with the responses I got:
|
||||
|
||||
**How can you assure users that they can rely on your service for their personal/business use?**
|
||||
|
||||
**Son Nguyen Kim:** SimpleLogin follows all the best practices in terms of [email deliverability][14] to reduce the emails ending up in the Spam folder. To mention a few:
|
||||
|
||||
* SPF, DKIM and strict DMARC
|
||||
* TLS everywhere
|
||||
* “Clean” IP: we made sure that our IP addresses are not blacklisted anywhere
|
||||
* Constant monitoring to avoid abuses.
|
||||
* Participate in email providers postmaster programs
|
||||
|
||||
|
||||
|
||||
**How sustainable is your business currently? **
|
||||
|
||||
**Son Nguyen Kim:** Though in Beta, we already have paying customers. They use SimpleLogin both personally (to protect privacy) and for their business (create emails with their domains).
|
||||
|
||||
**What features have you planned for the future?**
|
||||
|
||||
**Son Nguyen Kim**: An iOS app is already in progress, the Android app will follow just after.
|
||||
|
||||
* [PGP][15] to encrypt emails
|
||||
* Able to strip images from emails. Email tracking is usually done [using a 1-pixel image][16] so tracking will also be removed with this feature enabled.
|
||||
* [U2F][17] support (Yubikey)
|
||||
* Better integration with existing email infrastructure for people who want to self-host SimpleLogin
|
||||
|
||||
|
||||
|
||||
You can also find a public roadmap to their plans on [Trello][18].
|
||||
|
||||
**Wrapping Up**
|
||||
|
||||
Personally, I would really love to see this succeed as a privacy-friendly alternative to social network sign-up options implemented on various web services.
|
||||
|
||||
In addition to that, as it stands now as a service to generate alias email – that should suffice a lot of users who do not want to share their real email address. My initial impressions on SimpleLogin’s beta phase is quite positive. I’d recommend you to give it a try!
|
||||
|
||||
They also have a [Patreon][19] page – if you wish to donate instead of opting for a paying customer to help the development of SimpleLogin.
|
||||
|
||||
Have you tried something like this before? How exciting do you think SimpleLogin is? Feel free to share your thoughts in the comments.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/simplelogin/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://support.apple.com/en-us/HT210425
|
||||
[2]: https://simplelogin.io/
|
||||
[3]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/01/simplelogin-website.jpg?ssl=1
|
||||
[4]: https://github.com/simple-login/app
|
||||
[5]: https://itsfoss.com/secure-private-email-services/
|
||||
[6]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/best-vpn-linux.png?fit=800%2C450&ssl=1
|
||||
[7]: https://itsfoss.com/best-vpn-linux/
|
||||
[8]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/01/simplelogin-settings.jpg?ssl=1
|
||||
[9]: https://docs.simplelogin.io/
|
||||
[10]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/01/simplelogin-details.png?ssl=1
|
||||
[11]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/01/simplelogin-dashboard.jpg?ssl=1
|
||||
[12]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/01/simplelogin-extensions.jpg?ssl=1
|
||||
[13]: https://twitter.com/nguyenkims
|
||||
[14]: https://blog.hubspot.com/marketing/email-delivery-deliverability
|
||||
[15]: https://www.openpgp.org/
|
||||
[16]: https://www.theverge.com/2019/7/3/20681508/tracking-pixel-email-spying-superhuman-web-beacon-open-tracking-read-receipts-location
|
||||
[17]: https://en.wikipedia.org/wiki/Universal_2nd_Factor
|
||||
[18]: https://trello.com/b/4d6A69I4/open-roadmap
|
||||
[19]: https://www.patreon.com/simplelogin
|
||||
@@ -1,107 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (OpenCensus to monitor your Kubernetes cluster)
|
||||
[#]: via: (https://opensource.com/article/20/2/kubernetes-opencensus)
|
||||
[#]: author: (Yuri Grinshteyn https://opensource.com/users/yuri-grinshteyn)
|
||||
|
||||
OpenCensus to monitor your Kubernetes cluster
|
||||
======
|
||||
Learn how to use OpenCensus, a set of open source libraries for
|
||||
observability instrumentation and metrics tracing.
|
||||
![Ship captain sailing the Kubernetes seas][1]
|
||||
|
||||
In my last article in this series, I [introduced monitoring with Prometheus][2], the leading open source metric instrumentation, collection, and storage toolkit. While Prometheus has become the de facto standard for monitoring Kubernetes for many users, there may be reasons why you might choose another approach for metric telemetry.
|
||||
|
||||
One reason is that using Prometheus introduces another component in your cluster that needs to be maintained and updated and will require additional management to ensure data persistence over the long term. Another reason is that Prometheus collects an incredibly large set of metrics right out of the box, and this could become cost-prohibitive in situations where metric volume is an input into your overall observability costs.
|
||||
|
||||
This article will introduce you to [OpenCensus][3], a set of open source libraries for observability instrumentation. OpenCensus is the currently recommended library to use for instrumenting services to collect traces and metrics. The OpenTracing and OpenCensus projects have been merged into [OpenTelemetry][4], which will become the recommended library.
|
||||
|
||||
While OpenCensus enables both metrics and distributed tracing, this article focuses on metrics by:
|
||||
|
||||
* Describing the OpenCensus approach to instrumentation and its data model
|
||||
* Walking through a tutorial to explain how to instrument an application, deploy a sample application, and review the metrics that you can create with OpenCensus
|
||||
|
||||
|
||||
|
||||
I will revisit tracing in a future article.
|
||||
|
||||
### OpenCensus basics
|
||||
|
||||
OpenCensus' implementation depends on three core components:
|
||||
|
||||
* The [instrumentation][5] to create metrics and record data points (varies by language)
|
||||
* An [exporter][6] to send metric data to a storage backend (varies by language)
|
||||
* The backend to store metrics and enable querying metric data (varies by database)
|
||||
|
||||
|
||||
|
||||
To use OpenCensus in your application to record custom metrics, you will need to understand these elements for your particular programming languages and infrastructure.
|
||||
|
||||
#### Instrumentation
|
||||
|
||||
To understand how to instrument your application, you need first to understand OpenCensus' primitives, which are **measurements**, **measures**, **views**, and **aggregations**.
|
||||
|
||||
* **Measurement:** A measurement is the most fundamental entity; it's the single data point collected that represents a value at a point in time. For example, for a latency metric measured in milliseconds (ms), a measurement of 100 could represent an event with 100ms latency.
|
||||
* **Measure:** A measure represents a metric to be recorded. For example, you could use a "latency" measure to record HTTP response latency from your service. A measure is made up of a name, a description, and the units that the metric uses. For example, to measure latency, you might specify:
|
||||
* Name: response_latency
|
||||
* Description: latency of server response in ms
|
||||
* Unit: ms
|
||||
* **View:** A view is the combination of a measure, an aggregation, and optional tags. Views are the mechanism you'll use to connect to an exporter to send the captured values to a storage backend. A view includes:
|
||||
* Name
|
||||
* Description
|
||||
* The measure that will produce measurements for this collection
|
||||
* TagKeys, if you're using tags
|
||||
* **Aggregations:** Each view is also required to specify an aggregation; that is, how the view will treat multiple measures. Aggregations can be one of the following:
|
||||
* Count: The count of the number of measurement points in the view
|
||||
* Distribution: Histogram distribution of the points in the view
|
||||
* Sum: A sum of the values of the measurement points
|
||||
* LastValue: Only the last recorded value in the measurement
|
||||
|
||||
|
||||
|
||||
You can also refer to OpenCensus' [source][7] for additional information about the primitives.
|
||||
|
||||
#### Exporters
|
||||
|
||||
Once you have written the instrumentation to create measures, capture measurements, and aggregate them into views, you need an exporter to send your recorded metric data to your chosen storage backend. Unlike Prometheus, where you expose a dedicated metric endpoint to be scraped, OpenCensus works on a push model—the exporter pushes your collected data to the specified backend. You need to [choose the exporter][6] based on:
|
||||
|
||||
* The language that your application and instrumentation are written in
|
||||
* Available support for stats (metrics)
|
||||
* The available backend options
|
||||
|
||||
|
||||
|
||||
Using an exporter requires instantiating it in your code, registering it, and then registering your view to have the exporter send the collected data to the backend.
|
||||
|
||||
### Leverage metrics in OpenCensus
|
||||
|
||||
Now that you understand the terminology for how OpenCensus works to collect and export metrics, the next thing to learn is how metrics in OpenCensus function. Unlike [Prometheus][8], where you have to define the metric kind upfront, OpenCensus simply requires you to _collect_ the measurements and then _aggregate_ them in the _view_ before sending them to the _exporter_. The measurements support integer and float values. From there, you can use create histograms using the _distribution_ aggregation, add up the number of samples using the _count_ aggregation, or add up the collected values using the _sum_ aggregation.
|
||||
|
||||
Now you have a basic understanding of what OpenCensus is, how it works, and the kinds of data it can collect and store. Download your favorite tooling (or use [my tutorial here][9] and a [quickstart lab][10]) and take OpenCensus for a spin.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/2/kubernetes-opencensus
|
||||
|
||||
作者:[Yuri Grinshteyn][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/yuri-grinshteyn
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/ship_captain_devops_kubernetes_steer.png?itok=LAHfIpek (Ship captain sailing the Kubernetes seas)
|
||||
[2]: https://opensource.com/article/19/11/introduction-monitoring-prometheus
|
||||
[3]: https://opencensus.io/
|
||||
[4]: https://opentelemetry.io/
|
||||
[5]: https://github.com/census-instrumentation
|
||||
[6]: https://opencensus.io/exporters/supported-exporters/
|
||||
[7]: https://opencensus.io/stats/
|
||||
[8]: https://prometheus.io/docs/concepts/metric_types/
|
||||
[9]: https://github.com/yuriatgoogle/stack-doctor
|
||||
[10]: https://google.qwiklabs.com/
|
||||
@@ -1,84 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Introducing Zuul for improved CI/CD)
|
||||
[#]: via: (https://opensource.com/article/20/2/zuul)
|
||||
[#]: author: (Jeremy Stanley https://opensource.com/users/fungi)
|
||||
|
||||
Introducing Zuul for improved CI/CD
|
||||
======
|
||||
A quick history of how and why Zuul is replacing Jenkins in CI testing
|
||||
in the OpenStack community.
|
||||
![Plumbing tubes in many directions][1]
|
||||
|
||||
[Jenkins][2] is a marvelous piece of software. As an execution and automation engine, it's one of the best you're going to find. Jenkins serves as a key component in countless continuous integration (CI) systems, and this is a testament to the value of what its community has built over the years. But that's what it is—a component. Jenkins is not a CI system itself; it just runs things for you. It does that really well and has a variety of built-ins and a vibrant ecosystem of plugins to help you tell it what to run, when, and where.
|
||||
|
||||
CI is, at the most fundamental level, about integrating the work of multiple software development streams into a coherent whole with as much frequency and as little friction as possible. Jenkins, on its own, doesn't know about your source code or how to merge it together, nor does it know how to give constructive feedback to you and your colleagues. You can, of course, glue it together with other software that can perform these activities, and this is how many CI systems incorporate Jenkins.
|
||||
|
||||
It's what we did for OpenStack, too, at least at first.
|
||||
|
||||
### If it's not tested, it's broken
|
||||
|
||||
In 2010, an open source community of projects called [OpenStack][3] was forming. Some of the developers brought in to assist with the collaboration infrastructure also worked on a free database project called [Drizzle][4], and a key philosophy within that community was the idea "if it's not tested, it's broken." So OpenStack, on day one, required all proposed changes of its software to be reviewed and tested for regressions before they could be approved to merge into the trunk of any source code repositories. To do this, Hudson (which later forked to form the Jenkins project) was configured to run tests exercising every change.
|
||||
|
||||
A plugin was installed to interface with the [Gerrit][5] code review system, automatically triggering jobs when new changes were proposed and reporting back with review comments indicating whether they succeeded or failed. This may sound rudimentary by today's standards, but at the time, it was a revolutionary advancement for an open source collaboration. No developer on OpenStack was special in the eyes of CI, and everyone's changes had to pass this growing battery of tests before they could merge—a concept the project called "project gating."
|
||||
|
||||
There was, however, an emerging flaw with this gating idea: To guarantee two unrelated changes didn't alter a piece of software in functionally incompatible ways, they had to be tested one at a time in sequence before they could merge. OpenStack was complicated to install and test, even back then, and quickly grew in popularity. The rising volume of developer contributions coupled with increasing test coverage meant that, during busy periods, there was simply not enough time to test every change that passed review. Some longer-running jobs took nearly an hour to complete, so the upper bound for what could get through the gate was roughly two dozen changes in a day. The resulting merge backlog showed a new solution was required.
|
||||
|
||||
### Enter Zuul
|
||||
|
||||
During an OpenStack CI meeting in May 2012, one of the CI team members, James Blair, [announced][6] that he'd "been working on speculative execution of Jenkins jobs." **Speculative execution** is an optimization most commonly found in the pipelines of modern microprocessors. Much like the analogy with processor hardware, the theory was that by optimistically predicting positive gating results for changes recently approved but that had not yet completed their tests, subsequently approved changes could be tested concurrently and then conditionally merged as long as their predecessors also passed tests and merged. James said he had a name for this intelligent scheduler: [Zuul][7].
|
||||
|
||||
Within this time frame, challenges from trying to perform better revision control for Jenkins' XML job configuration led to the creation of the human-readable YAML-based [Jenkins Job Builder][8] templating engine. Limited success with the JClouds plugin for Jenkins and cumbersome attempts to use jobs for refreshing cloud images of single-use Jenkins slaves ended with the creation of the [Nodepool][9] service. Limited log-storage capabilities resulted in the team adding separate external solutions for organizing, serving, and indexing job logs and assuming maintainership of an abandoned secure copy protocol (SCP) plugin replacing the less-secure FTP option that Jenkins provided out of the box. The OpenStack infrastructure team was slowly building a fleet of services and utilities around Jenkins but began to bump up against a performance limitation.
|
||||
|
||||
### Multiplying Jenkins
|
||||
|
||||
By mid-2013, Nodepool was constantly recycling as many as 100 virtual machines registered with Jenkins as slaves, but this was no longer enough to keep up with the growing workload. Thread contention for global locks in Jenkins thwarted all attempts to push past this threshold, no matter how much processor power and memory was thrown at the master server. The project had offers to donate additional capacity for Jenkins slaves to help relieve the frequent job backlog, but this would require an additional Jenkins master. The efficient division of work between multiple masters needed a new channel of communication for dispatch and coordination of jobs. Zuul's maintainers identified the [Gearman][10] job server protocol as an ideal fit, so they outfitted Zuul with a new [geard][11] service and extended Jenkins with a custom Gearman client plugin.
|
||||
|
||||
Now that jobs were spread across a growing assembly of Jenkins masters, there was no longer any single dashboard with a complete view of job activity and results. In order to facilitate this new multi-master world, Zuul grew its own status API and WebUI, as well as a feature to emit metrics through the [StatsD][12] protocol. Over the next few years, Zuul steadily subsumed more of the CI features its users relied on, while Jenkins' place in the system waned accordingly, and it was becoming a liability. OpenStack made an early choice to standardize on the Python programming language; this was reflected in Zuul's development, yet Jenkins and its plugins were implemented in Java. Zuul's configuration was maintained in the same YAML serialization format that OpenStack used to template its own Jenkins jobs, while Jenkins kept everything in baroque XML. These differences complicated ongoing maintenance and led to an unnecessarily steep learning curve for new administrators from related communities that had started trying to run Zuuls.
|
||||
|
||||
The time was right for another revolution.
|
||||
|
||||
### The rise of Ansible
|
||||
|
||||
In early 2016, Zuul's maintainers embarked on an ambitious year-long overhaul of their growing fleet of services with the goal of eliminating Jenkins from the overall system design. By this time, Jenkins was serving only as a conduit for running jobs consisting mostly of shell scripts on slave nodes over SSH, providing real-time streaming of job output and copying resulting artifacts to longer-term storage. [Ansible][13] was found to be a great fit for that first need; purpose-built to run commands remotely over SSH, it was written in Python, just like Zuul, and also used YAML to define its tasks. It even had built-in modules for features the team had previously implemented as bespoke Jenkins plugins. Ansible provided true multi-node support right out of the box, so the same playbooks could be used for both simulating and performing complex production deployments. An ever-expanding ecosystem of third-party modules filled in any gaps, in much the same way as the Jenkins community's plugins had before.
|
||||
|
||||
A new Zuul executor service filled the prior role of the Jenkins master: it acted on pending requests in the scheduler's geard, dispatched them via Ansible to ephemeral servers managed by Nodepool, then collected results and artifacts for publication. It also exposed in-progress build output over the classic [RFC 742 Name/Finger protocol][14], streamed in real time from an extension of Ansible's command output module. Once it was no longer necessary to limit jobs to what Jenkins' parser could comprehend, Zuul was free to grow new features like distributed in-repository job definitions, shareable between projects with inheritance and secure handling of secrets, as well as the ability to test-drive proposed changes for the jobs themselves. Jenkins served its purpose admirably, but at least for Zuul, its usefulness was finally at an end.
|
||||
|
||||
### Testing the future
|
||||
|
||||
Zuul's community likes to say that it "tests the future" through its novel application of speculative execution. Gone are the harrowing days of wondering whether the improvement you want to make to an existing job will render it non-functional once it's applied in production. Overloaded review teams for a massive central job repository are a thing of the past. Jobs are treated as a part of the software and shipped right alongside the rest of the source code, taking advantage of Zuul's other features like cross-repository dependencies so that your change to part of a job in one project can be exercised with a proposed job change in another project. It will even comment on your job changes, highlighting specific lines with syntax problems as if it were another code reviewer giving you advice.
|
||||
|
||||
These were features Zuul only dreamed of before, but which required freedom from Jenkins so that it could take job parsing into its own hands. This is the future of CI, and Zuul's users are living it.
|
||||
|
||||
As of early 2019, the OpenStack Foundation recognized Zuul as an independent, openly governed project with its own identity and flourishing community. If you're into open source CI, consider taking a look. Development on the next evolution of Zuul is always underway, and you're welcome to help. Find out more on [Zuul's website][7].
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/2/zuul
|
||||
|
||||
作者:[Jeremy Stanley][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/fungi
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/plumbing_pipes_tutorial_how_behind_scenes.png?itok=F2Z8OJV1 (Plumbing tubes in many directions)
|
||||
[2]: https://jenkins.io/
|
||||
[3]: https://www.openstack.org/
|
||||
[4]: https://en.wikipedia.org/wiki/Drizzle_(database_server)
|
||||
[5]: https://www.gerritcodereview.com/
|
||||
[6]: http://eavesdrop.openstack.org/irclogs/%23openstack-meeting/%23openstack-meeting.2012-05-22.log.html#t2012-05-22T19:42:27
|
||||
[7]: https://zuul-ci.org/
|
||||
[8]: https://jenkins-job-builder.readthedocs.io/
|
||||
[9]: https://zuul-ci.org/docs/nodepool/
|
||||
[10]: http://gearman.org/
|
||||
[11]: https://docs.opendev.org/opendev/gear/#server-example
|
||||
[12]: https://github.com/statsd/statsd
|
||||
[13]: https://www.ansible.com/
|
||||
[14]: https://tools.ietf.org/html/rfc742
|
||||
@@ -1,210 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Automate your live demos with this shell script)
|
||||
[#]: via: (https://opensource.com/article/20/2/live-demo-script)
|
||||
[#]: author: (Lisa Seelye https://opensource.com/users/lisa)
|
||||
|
||||
Automate your live demos with this shell script
|
||||
======
|
||||
Try this script the next time you give a presentation to prevent making
|
||||
typos in front of a live audience.
|
||||
![Person using a laptop][1]
|
||||
|
||||
I gave a talk about [multi-architecture container images][2] at [LISA19][3] in October that included a lengthy live demo. Rather than writing out 30+ commands and risking typos, I decided to automate the demo with a shell script.
|
||||
|
||||
The script mimics what appears as input/output and runs the real commands in the background, pausing at various points so I can narrate what is going on. I'm very pleased with how the script turned out and the effect on stage. The script and supporting materials for my presentation are available on [GitHub][4] under an Apache 2.0 license.
|
||||
|
||||
### The script
|
||||
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
IMG=thedoh/lisa19
|
||||
REGISTRY=docker.io
|
||||
VERSION=19.10.1
|
||||
|
||||
# Plan B with GCR:
|
||||
#IMG=dulcet-iterator-213018
|
||||
#REGISTRY=us.gcr.io
|
||||
#VERSION=19.10.1
|
||||
|
||||
pause() {
|
||||
local step="${1}"
|
||||
ps1
|
||||
echo -n "# Next step: ${step}"
|
||||
read
|
||||
}
|
||||
|
||||
ps1() {
|
||||
echo -ne "\033[01;32m${USER}@$(hostname -s) \033[01;34m$(basename $(pwd)) \$ \033[00m"
|
||||
}
|
||||
|
||||
echocmd() {
|
||||
echo "$(ps1)$@"
|
||||
}
|
||||
|
||||
docmd() {
|
||||
echocmd $@
|
||||
$@
|
||||
}
|
||||
|
||||
step0() {
|
||||
local registry="${1}" img="${2}" version="${3}"
|
||||
# Mindful of tokens in ~/.docker/config.json
|
||||
docmd grep experimental ~/.docker/config.json
|
||||
|
||||
docmd cd ~/go/src/github.com/lisa/lisa19-containers
|
||||
|
||||
pause "This is what we'll be building"
|
||||
docmd export REGISTRY=${registry}
|
||||
docmd export IMG=${img}
|
||||
docmd export VERSION=${version}
|
||||
docmd make REGISTRY=${registry} IMG=${img} VERSION=${version} clean
|
||||
}
|
||||
|
||||
step1() {
|
||||
local registry="${1}" img="${2}" version="${3}"
|
||||
|
||||
docmd docker build --no-cache --platform=linux/amd64 --build-arg=GOARCH=amd64 -t ${REGISTRY}/${IMG}:amd64-${VERSION} .
|
||||
pause "ARM64 image next"
|
||||
docmd docker build --no-cache --platform=linux/arm64 --build-arg=GOARCH=arm64 -t ${REGISTRY}/${IMG}:arm64-${VERSION} .
|
||||
}
|
||||
|
||||
step2() {
|
||||
local registry="${1}" img="${2}" version="${3}" origpwd=$(pwd) savedir=$(mktemp -d) jsontemp=$(mktemp -t XXXXX)
|
||||
chmod 700 $jsontemp $savedir
|
||||
# Set our way back home and get ready to fix our arm64 image to amd64.
|
||||
echocmd 'origpwd=$(pwd)'
|
||||
echocmd 'savedir=$(mktemp -d)'
|
||||
echocmd "mkdir -p \$savedir/change"
|
||||
mkdir -p $savedir/change &>/dev/null
|
||||
echocmd "docker save ${REGISTRY}/${IMG}:arm64-${VERSION} 2>/dev/null 1> \$savedir/image.tar"
|
||||
docker save ${REGISTRY}/${IMG}:arm64-${VERSION} 2>/dev/null 1> $savedir/image.tar
|
||||
pause "untar the image to access its metadata"
|
||||
|
||||
echocmd "cd \$savedir/change"
|
||||
cd $savedir/change
|
||||
echocmd tar xf \$savedir/image.tar
|
||||
tar xf $savedir/image.tar
|
||||
docmd ls -l
|
||||
|
||||
pause "find the JSON config file"
|
||||
echocmd 'jsonfile=$(jq -r ".[0].Config" manifest.json)'
|
||||
jsonfile=$(jq -r ".[0].Config" manifest.json)
|
||||
|
||||
pause "notice the original metadata says amd64"
|
||||
echocmd jq '{architecture: .architecture, ID: .config.Image}' \$jsonfile
|
||||
jq '{architecture: .architecture, ID: .config.Image}' $jsonfile
|
||||
|
||||
pause "Change from amd64 to arm64 using a temp file"
|
||||
echocmd "jq '.architecture = \"arm64\"' \$jsonfile > \$jsontemp"
|
||||
jq '.architecture = "arm64"' $jsonfile > $jsontemp
|
||||
echocmd /bin/mv -f -- \$jsontemp \$jsonfile
|
||||
/bin/mv -f -- $jsontemp $jsonfile
|
||||
|
||||
pause "Check to make sure the config JSON file says arm64 now"
|
||||
echocmd jq '{architecture: .architecture, ID: .config.Image}' \$jsonfile
|
||||
jq '{architecture: .architecture, ID: .config.Image}' $jsonfile
|
||||
|
||||
pause "delete the image with the incorrect metadata"
|
||||
docmd docker rmi ${REGISTRY}/${IMG}:arm64-${VERSION}
|
||||
|
||||
pause "Re-compress the ARM64 image and load it back into Docker, then clean up the temp space"
|
||||
echocmd 'tar cf - * | docker load'
|
||||
tar cf - * | docker load
|
||||
|
||||
docmd cd $origpwd
|
||||
echocmd "/bin/rm -rf -- \$savedir"
|
||||
/bin/rm -rf -- $savedir &>/dev/null
|
||||
}
|
||||
|
||||
step3() {
|
||||
local registry="${1}" img="${2}" version="${3}"
|
||||
docmd docker push ${registry}/${img}:amd64-${version}
|
||||
pause "push ARM64 image to ${registry}"
|
||||
docmd docker push ${registry}/${img}:arm64-${version}
|
||||
}
|
||||
|
||||
step4() {
|
||||
local registry="${1}" img="${2}" version="${3}"
|
||||
docmd docker manifest create ${registry}/${img}:${version} ${registry}/${img}:arm64-${version} ${registry}/${img}:amd64-${version}
|
||||
|
||||
pause "add a reference to the amd64 image to the manifest list"
|
||||
docmd docker manifest annotate ${registry}/${img}:${version} ${registry}/${img}:amd64-${version} --os linux --arch amd64
|
||||
pause "now add arm64"
|
||||
docmd docker manifest annotate ${registry}/${img}:${version} ${registry}/${img}:arm64-${version} --os linux --arch arm64
|
||||
}
|
||||
|
||||
step5() {
|
||||
local registry="${1}" img="${2}" version="${3}"
|
||||
docmd docker manifest push ${registry}/${img}:${version}
|
||||
}
|
||||
|
||||
step6() {
|
||||
local registry="${1}" img="${2}" version="${3}"
|
||||
docmd make REGISTRY=${registry} IMG=${img} VERSION=${version} clean
|
||||
|
||||
pause "ask docker.io if ${img}:${version} has a linux/amd64 manifest, and run it"
|
||||
docmd docker pull --platform linux/amd64 ${registry}/${img}:${version}
|
||||
docmd docker run --rm -i ${registry}/${img}:${version}
|
||||
|
||||
pause "clean slate again"
|
||||
docmd make REGISTRY=${registry} IMG=${img} VERSION=${version} clean
|
||||
|
||||
pause "now repeat for linux/arm64 and see what it gives us"
|
||||
docmd docker pull --platform linux/arm64 ${registry}/${img}:${version}
|
||||
set +e
|
||||
docmd docker run --rm -i ${registry}/${img}:${version}
|
||||
set -e
|
||||
if [[ $(uname -s) == "Darwin" ]]; then
|
||||
pause "note about Docker on Mac and binfmt_misc: binfmt_misc lets a mac run arm64 binaries in the Docker VM"
|
||||
fi
|
||||
}
|
||||
|
||||
pause "initial setup"
|
||||
step0 ${REGISTRY} ${IMG} ${VERSION}
|
||||
pause "1 build constituent images"
|
||||
step1 ${REGISTRY} ${IMG} ${VERSION}
|
||||
|
||||
pause "2 fix ARM64 metadata"
|
||||
step2 ${REGISTRY} ${IMG} ${VERSION}
|
||||
|
||||
pause "3 push constituent images up to docker.io"
|
||||
step3 ${REGISTRY} ${IMG} ${VERSION}
|
||||
|
||||
pause "4 build the manifest list for the image"
|
||||
step4 ${REGISTRY} ${IMG} ${VERSION}
|
||||
|
||||
pause "5 Push the manifest list to docker.io"
|
||||
step5 ${REGISTRY} ${IMG} ${VERSION}
|
||||
|
||||
pause "6 clean slate, and validate the list-based image"
|
||||
step6 ${REGISTRY} ${IMG} ${VERSION}
|
||||
|
||||
docmd echo 'Manual steps all done!'
|
||||
make REGISTRY=${REGISTRY} IMG=${IMG} VERSION=${VERSION} clean &>/dev/null
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/2/live-demo-script
|
||||
|
||||
作者:[Lisa Seelye][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/lisa
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/laptop_screen_desk_work_chat_text.png?itok=UXqIDRDD (Person using a laptop)
|
||||
[2]: https://www.usenix.org/conference/lisa19/presentation/seelye
|
||||
[3]: https://www.usenix.org/conference/lisa19
|
||||
[4]: https://github.com/lisa/lisa19-containers
|
||||
@@ -1,685 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Manage your SSL certificates with the ssl-on-demand script)
|
||||
[#]: via: (https://opensource.com/article/20/2/ssl-demand)
|
||||
[#]: author: (Abhishek Tamrakar https://opensource.com/users/tamrakar)
|
||||
|
||||
Manage your SSL certificates with the ssl-on-demand script
|
||||
======
|
||||
Keep track of certificate expirations to prevent problems with the
|
||||
ssl-on-demand script.
|
||||
![Lock][1]
|
||||
|
||||
It happens all the time, to the largest of companies. An important certificate doesn't get renewed, and services become inaccessible. It happened to Microsoft Teams in early February 2020, awkwardly timed just after the launch of a major television campaign promoting it as a [Slack competitor][2]. Embarrassing as that may be, it's sure to happen to someone else in the future.
|
||||
|
||||
On the modern web, expired [certificates][3] can create major problems for websites, ranging from unhappy users who can't connect to a site to security threats from bad actors who take advantage of the failure to renew a certificate.
|
||||
|
||||
[Ssl-on-demand][4] is a set of SSL scripts to help site owners manage certificates. It is used for on-demand certificate generation and validation and it can create certificate signing requests ([CSRs][5]) and predict the expiration of existing certificates.
|
||||
|
||||
### Automate SSL expiry checks
|
||||
|
||||
|
||||
```
|
||||
USAGE: SSLexpiryPredictions.sh -[cdewh]
|
||||
|
||||
DESCRIPTION: This script predicts the expiring SSL certificates based on the end date.
|
||||
|
||||
OPTIONS:
|
||||
|
||||
-c| sets the value for configuration file which has server:port or host:port details.
|
||||
|
||||
-d| sets the value of directory containing the certificate files in crt or pem format.
|
||||
|
||||
-e| sets the value of certificate extention, e.g crt, pem, cert.
|
||||
crt: default [to be used with -d, if certificate file extention is other than .crt]
|
||||
|
||||
-w| sets the value for writing the script output to a file.
|
||||
|
||||
-h| prints this help and exit.
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
|
||||
To create a file with a list of all servers and their port numbers to make an SSL handshake, use:
|
||||
|
||||
|
||||
```
|
||||
cat > servers.list
|
||||
server1:port1
|
||||
server2:port2
|
||||
server3:port3
|
||||
(ctrl+d)
|
||||
|
||||
$ ./SSLexpiryPredictions.sh -c server.list
|
||||
```
|
||||
|
||||
Run the script by providing the certificate location and extension (in case it is not .crt):
|
||||
|
||||
|
||||
```
|
||||
`$ ./SSLexpiryPredictions.sh -d /path/to/certificates/dir -e pem`
|
||||
```
|
||||
|
||||
### Automate CSR and private key creation
|
||||
|
||||
|
||||
```
|
||||
Usage: genSSLcsr.sh [options] -[cdmshx]
|
||||
[-c (common name)]
|
||||
[-d (domain name)]
|
||||
[-s (SSL certificate subject)]
|
||||
[-p (password)]
|
||||
[-m (email address)] *(Experimental)
|
||||
[-r (remove pasphrase) default:true]
|
||||
[-h (help)]
|
||||
[-x (optional)]
|
||||
|
||||
[OPTIONS]
|
||||
-c| Sets the value for common name.
|
||||
A valid common name is something that ends with 'xyz.com'
|
||||
|
||||
-d| Sets the domain name.
|
||||
|
||||
-s| Sets the subject to be applied to the certificates.
|
||||
'/C=country/ST=state/L=locality/O=organization/OU=organizationalunit/emailAddress=email'
|
||||
|
||||
-p| Sets the password for private key.
|
||||
|
||||
-r| Sets the value of remove passphrase.
|
||||
true:[default] passphrase will be removed from key.
|
||||
false: passphrase will not be removed and key wont get printed.
|
||||
|
||||
-m| Sets the mailing capability to the script.
|
||||
(Experimental at this time and requires a lot of work)
|
||||
|
||||
-x| Creates the certificate request and key but do not print on screen.
|
||||
To be used when script is used just to create the key and CSR with no need
|
||||
+ to generate the certficate on the go.
|
||||
|
||||
-h| Displays the usage. No further functions are performed.
|
||||
|
||||
Example: genSSLcsr.sh -c mywebsite.xyz.com -m [myemail@mydomain.com][6]
|
||||
```
|
||||
|
||||
### The scripts
|
||||
|
||||
#### 1. SSLexpiryPredictions.sh
|
||||
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
##############################################
|
||||
#
|
||||
# PURPOSE: The script to predict expiring SSL certificates.
|
||||
#
|
||||
# AUTHOR: 'Abhishek.Tamrakar'
|
||||
#
|
||||
# VERSION: 0.0.1
|
||||
#
|
||||
# COMPANY: Self
|
||||
#
|
||||
# EMAIL: [abhishek.tamrakar08@gmail.com][7]
|
||||
#
|
||||
# GENERATED: on 2018-05-20
|
||||
#
|
||||
# LICENSE: Copyright (C) 2018 Abhishek Tamrakar
|
||||
#
|
||||
# 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.
|
||||
##############################################
|
||||
|
||||
#your Variables go here
|
||||
script=${0##/}
|
||||
exitcode=''
|
||||
WRITEFILE=0
|
||||
CONFIG=0
|
||||
DIR=0
|
||||
# functions here
|
||||
usage()
|
||||
{
|
||||
cat <<EOF
|
||||
|
||||
USAGE: $script -[cdewh]"
|
||||
|
||||
DESCRIPTION: This script predicts the expiring SSL certificates based on the end date.
|
||||
|
||||
OPTIONS:
|
||||
|
||||
-c| sets the value for configuration file which has server:port or host:port details.
|
||||
|
||||
-d| sets the value of directory containing the certificate files in crt or pem format.
|
||||
|
||||
-e| sets the value of certificate extention, e.g crt, pem, cert.
|
||||
crt: default
|
||||
|
||||
-w| sets the value for writing the script output to a file.
|
||||
|
||||
-h| prints this help and exit.
|
||||
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
# print info messages
|
||||
info()
|
||||
{
|
||||
printf '\n%s: %6s\n' "INFO" "$@"
|
||||
}
|
||||
# print error messages
|
||||
error()
|
||||
{
|
||||
printf '\n%s: %6s\n' "ERROR" "$@"
|
||||
exit 1
|
||||
}
|
||||
# print warning messages
|
||||
warn()
|
||||
{
|
||||
printf '\n%s: %6s\n' "WARN" "$@"
|
||||
}
|
||||
# get expiry for the certificates
|
||||
getExpiry()
|
||||
{
|
||||
local expdate=$1
|
||||
local certname=$2
|
||||
today=$(date +%s)
|
||||
timetoexpire=$(( ($expdate - $today)/(60*60*24) ))
|
||||
|
||||
expcerts=( ${expcerts[@]} "${certname}:$timetoexpire" )
|
||||
}
|
||||
|
||||
# print all expiry that was found, typically if there is any.
|
||||
printExpiry()
|
||||
{
|
||||
local args=$#
|
||||
i=0
|
||||
if [[ $args -ne 0 ]]; then
|
||||
#statements
|
||||
printf '%s\n' "---------------------------------------------"
|
||||
printf '%s\n' "List of expiring SSL certificates"
|
||||
printf '%s\n' "---------------------------------------------"
|
||||
printf '%s\n' "$@" | \
|
||||
sort -t':' -g -k2 | \
|
||||
column -s: -t | \
|
||||
awk '{printf "%d.\t%s\n", NR, $0}'
|
||||
printf '%s\n' "---------------------------------------------"
|
||||
fi
|
||||
}
|
||||
|
||||
# calculate the end date for the certificates first, finally to compare and predict when they are going to expire.
|
||||
calcEndDate()
|
||||
{
|
||||
sslcmd=$(which openssl)
|
||||
if [[ x$sslcmd = x ]]; then
|
||||
#statements
|
||||
error "$sslcmd command not found!"
|
||||
fi
|
||||
# when cert dir is given
|
||||
if [[ $DIR -eq 1 ]]; then
|
||||
#statements
|
||||
checkcertexists=$(ls -A $TARGETDIR| egrep "*.$EXT$")
|
||||
if [[ -z ${checkcertexists} ]]; then
|
||||
#statements
|
||||
error "no certificate files at $TARGETDIR with extention $EXT"
|
||||
fi
|
||||
for file in $TARGETDIR/*.${EXT:-crt}
|
||||
do
|
||||
expdate=$($sslcmd x509 -in $file -noout -enddate)
|
||||
expepoch=$(date -d "${expdate##*=}" +%s)
|
||||
certificatename=${file##*/}
|
||||
getExpiry $expepoch ${certificatename%.*}
|
||||
done
|
||||
elif [[ $CONFIG -eq 1 ]]; then
|
||||
#statements
|
||||
while read line
|
||||
do
|
||||
if echo "$line" | \
|
||||
egrep -q '^[a-zA-Z0-9.]+:[0-9]+|^[a-zA-Z0-9]+_.*:[0-9]+';
|
||||
then
|
||||
expdate=$(echo | \
|
||||
openssl s_client -connect $line 2>/dev/null | \
|
||||
openssl x509 -noout -enddate 2>/dev/null);
|
||||
if [[ $expdate = '' ]]; then
|
||||
#statements
|
||||
warn "[error:0906D06C] Cannot fetch certificates for $line"
|
||||
else
|
||||
expepoch=$(date -d "${expdate##*=}" +%s);
|
||||
certificatename=${line%:*};
|
||||
getExpiry $expepoch ${certificatename};
|
||||
fi
|
||||
else
|
||||
warn "[format error] $line is not in required format!"
|
||||
fi
|
||||
done < $CONFIGFILE
|
||||
fi
|
||||
}
|
||||
# your script goes here
|
||||
while getopts ":c:d:w:e:h" options
|
||||
do
|
||||
case $options in
|
||||
c )
|
||||
CONFIG=1
|
||||
CONFIGFILE="$OPTARG"
|
||||
if [[ ! -e $CONFIGFILE ]] || [[ ! -s $CONFIGFILE ]]; then
|
||||
#statements
|
||||
error "$CONFIGFILE does not exist or empty!"
|
||||
fi
|
||||
;;
|
||||
e )
|
||||
EXT="$OPTARG"
|
||||
case $EXT in
|
||||
crt|pem|cert )
|
||||
info "Extention check complete."
|
||||
;;
|
||||
* )
|
||||
error "invalid certificate extention $EXT!"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
d )
|
||||
DIR=1
|
||||
TARGETDIR="$OPTARG"
|
||||
[ $TARGETDIR = '' ] && error "$TARGETDIR empty variable!"
|
||||
;;
|
||||
w )
|
||||
WRITEFILE=1
|
||||
OUTFILE="$OPTARG"
|
||||
;;
|
||||
h )
|
||||
usage
|
||||
;;
|
||||
\? )
|
||||
usage
|
||||
;;
|
||||
: )
|
||||
fatal "Argument required !!! see \'-h\' for help"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $(($OPTIND - 1))
|
||||
#
|
||||
calcEndDate
|
||||
#finally print the list
|
||||
if [[ $WRITEFILE -eq 0 ]]; then
|
||||
#statements
|
||||
printExpiry ${expcerts[@]}
|
||||
else
|
||||
printExpiry ${expcerts[@]} > $OUTFILE
|
||||
fi
|
||||
```
|
||||
|
||||
#### 2. genSSLcsr.sh
|
||||
|
||||
|
||||
```
|
||||
#!/bin/bash -
|
||||
#===============================================================================
|
||||
#
|
||||
# FILE: genSSLcsr.sh
|
||||
#
|
||||
# USAGE: ./genSSLcsr.sh [options]
|
||||
#
|
||||
# DESCRIPTION: ++++version 1.0.2
|
||||
# Fixed few bugs from previous script
|
||||
# +Removing passphrase after CSR generation
|
||||
# Extended use of functions
|
||||
# Checks for valid common name
|
||||
# ++++1.0.3
|
||||
# Fixed line breaks
|
||||
# Work directory to be created at the start
|
||||
# Used getopts for better code arrangements
|
||||
# ++++1.0.4
|
||||
# Added mail feature (experimental at this time and needs
|
||||
# a mail server running locally.)
|
||||
# Added domain input and certificate subject inputs
|
||||
#
|
||||
# OPTIONS: ---
|
||||
# REQUIREMENTS: openssl, mailx
|
||||
# BUGS: ---
|
||||
# NOTES: ---
|
||||
# AUTHOR: Abhishek Tamrakar (), [abhishek.tamrakar08@gmail.com][7]
|
||||
# ORGANIZATION: Self
|
||||
# CREATED: 6/24/2016
|
||||
# REVISION: 4
|
||||
# COPYRIGHT AND
|
||||
# LICENSE: Copyright (C) 2016 Abhishek Tamrakar
|
||||
#
|
||||
# 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.
|
||||
#===============================================================================
|
||||
|
||||
#variables ges here
|
||||
#set basename to scriptname
|
||||
SCRIPT=${0##*/}
|
||||
|
||||
#set flags
|
||||
TFOUND=0
|
||||
CFOUND=0
|
||||
MFOUND=0
|
||||
XFOUND=0
|
||||
SFOUND=0
|
||||
logdir=/var/log
|
||||
# edit these below values to replace with yours
|
||||
homedir=''
|
||||
yourdomain=''
|
||||
country=IN
|
||||
state=Maharashtra
|
||||
locality=Pune
|
||||
organization="your_organization"
|
||||
organizationalunit="your_organizational_unit"
|
||||
email=your_email@your_domain
|
||||
password=your_ssl_password
|
||||
# OS is declared and will be used in its next version
|
||||
OS=$(egrep -io 'Redhat|centos|fedora|ubuntu' /etc/issue)
|
||||
|
||||
### function declarations ###
|
||||
|
||||
info()
|
||||
{
|
||||
printf '\n%s\t%s\t' "INFO" "$@"
|
||||
}
|
||||
|
||||
#exit on error with a custom error message
|
||||
#the extra function was removed and replaced withonly one.
|
||||
#using FAILED\n\e<message> is a way but not necessarily required.
|
||||
#
|
||||
|
||||
fatal()
|
||||
{
|
||||
printf '\n%s\t%s\n' "ERROR" "$@"
|
||||
exit 1
|
||||
}
|
||||
|
||||
checkperms()
|
||||
{
|
||||
if [[ -z ${homedir} ]]; then
|
||||
homedir=$(pwd)
|
||||
fi
|
||||
if [[ -w ${homedir} ]]; then
|
||||
info "Permissions acquired for ${SCRIPT} on ${homedir}."
|
||||
else
|
||||
fatal "InSufficient permissions to run the ${SCRIPT}."
|
||||
fi
|
||||
}
|
||||
|
||||
checkDomain()
|
||||
{
|
||||
info "Initializing Domain ${cn} check ? "
|
||||
if [[ ! -z ${yourdomain} ]]; then
|
||||
workdir=${homedir}/${yourdomain}
|
||||
echo -e "${cn}"|grep -E -i -q "${yourdomain}$" && echo -n "[OK]" || fatal "InValid domain in ${cn}"
|
||||
else
|
||||
workdir=${homedir}/${cn#*.}
|
||||
echo -n "[NULL]"
|
||||
info "WARNING: No domain declared to check."
|
||||
confirmUserAction
|
||||
fi
|
||||
} # end function checkDomain
|
||||
|
||||
usage()
|
||||
{
|
||||
cat << EOF
|
||||
|
||||
Usage: $SCRIPT [options] -[cdmshx]
|
||||
[-c (common name)]
|
||||
[-d (domain name)]
|
||||
[-s (SSL certificate subject)]
|
||||
[-p (password)]
|
||||
[-m (email address)] *(Experimental)
|
||||
[-r (remove pasphrase) default:true]
|
||||
[-h (help)]
|
||||
[-x (optional)]
|
||||
|
||||
[OPTIONS]
|
||||
-c| Sets the value for common name.
|
||||
A valid common name is something that ends with 'xyz.com'
|
||||
|
||||
-d| Sets the domain name.
|
||||
|
||||
-s| Sets the subject to be applied to the certificates.
|
||||
'/C=country/ST=state/L=locality/O=organization/OU=organizationalunit/emailAddress=email'
|
||||
|
||||
-p| Sets the password for private key.
|
||||
|
||||
-r| Sets the value of remove passphrase.
|
||||
true:[default] passphrase will be removed from key.
|
||||
false: passphrase will not be removed and key wont get printed.
|
||||
|
||||
-m| Sets the mailing capability to the script.
|
||||
(Experimental at this time and requires a lot of work)
|
||||
|
||||
-x| Creates the certificate request and key but do not print on screen.
|
||||
To be used when script is used just to create the key and CSR with no need
|
||||
+ to generate the certficate on the go.
|
||||
|
||||
-h| Displays the usage. No further functions are performed.
|
||||
|
||||
Example: $SCRIPT -c mywebsite.xyz.com -m [myemail@mydomain.com][6]
|
||||
|
||||
EOF
|
||||
exit 1
|
||||
} # end usage
|
||||
|
||||
confirmUserAction() {
|
||||
while true; do
|
||||
read -p "Do you wish to continue? ans: " yn
|
||||
case $yn in
|
||||
[Yy]* ) info "Initiating the process";
|
||||
break;;
|
||||
[Nn]* ) exit 1;;
|
||||
* ) info "Please answer yes or no.";;
|
||||
esac
|
||||
done
|
||||
} # end function confirmUserAction
|
||||
|
||||
parseSubject()
|
||||
{
|
||||
local subject="$1"
|
||||
parsedsubject=$(echo $subject|sed 's/\// /g;s/^ //g')
|
||||
for i in ${parsedsubject}; do
|
||||
case ${i%=*} in
|
||||
'C' )
|
||||
country=${i##*=}
|
||||
;;
|
||||
'ST' )
|
||||
state=${i##*=}
|
||||
;;
|
||||
'L' )
|
||||
locality=${i##*=}
|
||||
;;
|
||||
'O' )
|
||||
organization=${i##*=}
|
||||
;;
|
||||
'OU' )
|
||||
organizationalunit=${i##*=}
|
||||
;;
|
||||
'emailAddress' )
|
||||
email=${i##*=}
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
sendMail()
|
||||
{
|
||||
mailcmd=$(which mailx)
|
||||
if [[ x"$mailcmd" = "x" ]]; then
|
||||
fatal "Cannot send email! please install mailutils for linux"
|
||||
else
|
||||
echo "SSL CSR attached." | $mailcmd -s "SSL certificate request" \
|
||||
-t $email $ccemail -A ${workdir}/${cn}.csr \
|
||||
&& info "mail sent" \
|
||||
|| fatal "error in sending mail."
|
||||
fi
|
||||
}
|
||||
|
||||
genCSRfile()
|
||||
{
|
||||
info "Creating signed key request for ${cn}"
|
||||
#Generate a key
|
||||
openssl genrsa -des3 -passout pass:$password -out ${workdir}/${cn}.key 4096 -noout 2>/dev/null && echo -n "[DONE]" || fatal "unable to generate key"
|
||||
|
||||
#Create the request
|
||||
info "Creating Certificate request for ${cn}"
|
||||
openssl req -new -key ${workdir}/${cn}.key -passin pass:$password -sha1 -nodes \
|
||||
-subj "/C=$country/ST=$state/L=$locality/O=$organization/OU=$organizationalunit/CN=$cn/emailAddress=$email" \
|
||||
-out ${workdir}/${cn}.csr && echo -n "[DONE]" || fatal "unable to create request"
|
||||
|
||||
if [[ "${REMOVEPASSPHRASE:-true}" = 'true' ]]; then
|
||||
#statements
|
||||
#Remove passphrase from the key. Comment the line out to keep the passphrase
|
||||
info "Removing passphrase from ${cn}.key"
|
||||
openssl rsa -in ${workdir}/${cn}.key \
|
||||
-passin pass:$password \
|
||||
-out ${workdir}/${cn}.insecure 2>/dev/null \
|
||||
&& echo -n "[DONE]" || fatal "unable to remove passphrase"
|
||||
#swap the filenames
|
||||
info "Swapping the ${cn}.key to secure"
|
||||
mv ${workdir}/${cn}.key ${workdir}/${cn}.secure \
|
||||
&& echo -n "[DONE]" || fatal "unable to perfom move"
|
||||
info "Swapping insecure key to ${cn}.key"
|
||||
mv ${workdir}/${cn}.insecure ${workdir}/${cn}.key \
|
||||
&& echo -n "[DONE]" || fatal "unable to perform move"
|
||||
else
|
||||
info "Flag '-r' is set, passphrase will not be removed."
|
||||
fi
|
||||
}
|
||||
|
||||
printCSR()
|
||||
{
|
||||
if [[ -e ${workdir}/${cn}.csr ]] && [[ -e ${workdir}/${cn}.key ]]
|
||||
then
|
||||
echo -e "\n\n----------------------------CSR-----------------------------"
|
||||
cat ${workdir}/${cn}.csr
|
||||
echo -e "\n----------------------------KEY-----------------------------"
|
||||
cat ${workdir}/${cn}.key
|
||||
echo -e "------------------------------------------------------------\n"
|
||||
else
|
||||
fatal "CSR or KEY generation failed !!"
|
||||
fi
|
||||
}
|
||||
|
||||
### END Functions ###
|
||||
|
||||
#Check the number of arguments. If none are passed, print help and exit.
|
||||
NUMARGS=$#
|
||||
if [ $NUMARGS -eq 0 ]; then
|
||||
fatal "$NUMARGS Arguments provided !!!! See usage with '-h'"
|
||||
fi
|
||||
|
||||
#Organisational details
|
||||
|
||||
while getopts ":c:d:sⓂ️p:rhx" atype
|
||||
do
|
||||
case $atype in
|
||||
c )
|
||||
CFOUND=1
|
||||
cn="$OPTARG"
|
||||
;;
|
||||
d )
|
||||
yourdomain="$OPTARG"
|
||||
;;
|
||||
s )
|
||||
SFOUND=1
|
||||
subj="$OPTARG"
|
||||
;;
|
||||
p )
|
||||
password="$OPTARG"
|
||||
;;
|
||||
r )
|
||||
REMOVEPASSPHRASE='false'
|
||||
;;
|
||||
m )
|
||||
MFOUND=1
|
||||
ccemail="$OPTARG"
|
||||
;;
|
||||
x )
|
||||
XFOUND=1
|
||||
;;
|
||||
h )
|
||||
usage
|
||||
;;
|
||||
\? )
|
||||
usage
|
||||
;;
|
||||
: )
|
||||
fatal "Argument required !!! see \'-h\' for help"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $(($OPTIND - 1))
|
||||
|
||||
#### END CASE #### START MAIN ####
|
||||
|
||||
if [ $CFOUND -eq 1 ]
|
||||
then
|
||||
# take current dir as homedir by default.
|
||||
checkperms ${homedir}
|
||||
checkDomain
|
||||
|
||||
if [[ ! -d ${workdir} ]]
|
||||
then
|
||||
mkdir ${workdir:-${cn#*.}} 2>/dev/null && info "${workdir} created."
|
||||
else
|
||||
info "${workdir} exists."
|
||||
fi # end workdir check
|
||||
parseSubject "$subj"
|
||||
genCSRfile
|
||||
if [ $XFOUND -eq 0 ]
|
||||
then
|
||||
sleep 2
|
||||
printCSR
|
||||
fi # end x check
|
||||
if [[ $MFOUND -eq 1 ]]; then
|
||||
sendMail
|
||||
fi
|
||||
else
|
||||
fatal "Nothing to do!"
|
||||
fi # end common name check
|
||||
|
||||
##### END MAIN #####
|
||||
```
|
||||
|
||||
* * *
|
||||
|
||||
_This was originally published as the README in [ssl-on-demand's GitHub repository][4] and is reused with permission._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/2/ssl-demand
|
||||
|
||||
作者:[Abhishek Tamrakar][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/tamrakar
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/security-lock-password.jpg?itok=KJMdkKum (Lock)
|
||||
[2]: https://opensource.com/alternatives/slack
|
||||
[3]: https://opensource.com/article/19/1/what-certificate
|
||||
[4]: https://github.com/abhiTamrakar/ssl-on-demand
|
||||
[5]: https://en.wikipedia.org/wiki/Certificate_signing_request
|
||||
[6]: mailto:myemail@mydomain.com
|
||||
[7]: mailto:abhishek.tamrakar08@gmail.com
|
||||
@@ -1,69 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Automating unit tests in test-driven development)
|
||||
[#]: via: (https://opensource.com/article/20/2/automate-unit-tests)
|
||||
[#]: author: (Alex Bunardzic https://opensource.com/users/alex-bunardzic)
|
||||
|
||||
Automating unit tests in test-driven development
|
||||
======
|
||||
What unit tests have in common with carpentry.
|
||||
![gears and lightbulb to represent innovation][1]
|
||||
|
||||
DevOps is a software engineering discipline focused on minimizing the lead time to achieve a desired business impact. While business stakeholders and sponsors have ideas on how to optimize business operations, those ideas need to be validated in the field. This means business automation (i.e., software products) must be placed in front of end users and paying customers. Only then will the business confirm whether the initial idea for improvement was fruitful or not.
|
||||
|
||||
Software engineering is a budding discipline, and it can get difficult to ship products that are defect-free. For that reason, DevOps resorts to maximizing automation. Any repeatable chore, such as testing implemented changes to the source code, should be automated by DevOps engineers.
|
||||
|
||||
This article looks at how to automate unit tests. These tests are focused on what I like to call "programming in the small." Much more important test automation (the so-called "programming in the large") must use a different discipline—integration testing. But that's a topic for another article.
|
||||
|
||||
### What is a unit?
|
||||
|
||||
When I'm teaching approaches to unit testing, often, my students cannot clearly determine what a testable unit is. Which is to say, the granularity of the processing is not always clear.
|
||||
|
||||
I like to point out that the easiest way to spot a valid unit is to think of it as a _unit of behavior_. For example (albeit a trivial one), when an authenticated customer begins online shopping, the unit of behavior is a cart that has zero items in it. Once we all agree that an empty shopping cart has zero items in it, we can focus on automating the unit test that will ensure that such a shopping cart always returns zero items.
|
||||
|
||||
### What is not a unit?
|
||||
|
||||
Any processing that involves more than a single behavior should not be viewed as a unit. For example, if shopping cart processing results in tallying up the number of items in the cart AND calculating the order total AND calculating sales tax AND calculating the suggested shipping method, that behavior is not a good candidate for unit testing. Such behavior is a good candidate for integration testing.
|
||||
|
||||
### When to write a unit test
|
||||
|
||||
There is a lot of debate about when to write a unit test. Received wisdom states that once the code has been written, it is a good idea to write automated scripts that will assert whether the implemented unit of behavior delivers functionality as expected. Not only does such a unit test (or a few unit tests) document the expected behavior, the collection of all unit tests ensures that future changes will not degrade quality. If a future change adversely affects the already implemented behavior, one or more unit tests will complain, which will alert developers that regression has occurred.
|
||||
|
||||
There is another way to look at software engineering. It is based on the traditional adage "measure twice, cut once." In that light, writing code before writing tests would be equivalent to cutting a part of some product (say, a chair leg) and measuring it only after it's cut. If the craftsperson doing the cutting is very skilled, that approach may work (kind of). But more likely than not, the chair legs cut this way would end up with unequal lengths. So, it is advisable to measure before cutting. What that means for the practice of software engineering is that the measurements are expressed in the unit tests. Once we measure the required values, we create a blueprint (a unit test). That blueprint is then used to guide the cutting of the code.
|
||||
|
||||
Common sense would suggest that it is more reasonable to measure first and, only then, do the cutting. According to that line of reasoning, writing unit tests before writing code is a recommended way to do proper software engineering. Technically speaking, this "measure twice, cut once" approach is called a "test-first" approach. The opposite approach, where we write the code first, is called "test-later." The test-first approach is the approach advocated by [test-driven development][2] (TDD) methodology. Writing tests later is called test-later development (TLD).
|
||||
|
||||
### Why is TLD harmful?
|
||||
|
||||
Cutting before measuring is not recommended. Even the most talented craftspeople will eventually make mistakes by cutting without doing so. A lack of measurement will eventually catch up with even the most experienced of us as we continue in our craft. So it's best to produce a blueprint (i.e., measurements) before cutting.
|
||||
|
||||
But that's not the only reason why the TLD approach is considered harmful. When we write code, we're simultaneously considering two separate concerns: the expected behavior of the code and the optimal structure of the code. These two concerns are very dissimilar. That fact makes it very challenging to do a proper job satisfying the expectations regarding both the desired behavior and the optimal (or at the very least, decent) code structure.
|
||||
|
||||
The TDD approach solves this conundrum by focusing undivided attention first on the expected desired behavior. We start by writing the unit test. In that test, we focus on _what_ we expect to happen. At this point, we don't care, in the least, _how_ the expected behavior is going to materialize.
|
||||
|
||||
Once we're done describing the _what_ (i.e., what manifest behavior are we expecting from the unit we are about to build?), we watch that expectation fail. It fails because the code that is concerned with _how_ the expected behavior is going to happen hasn't materialized yet. Now we are compelled to write the code that's going to take care of the _how_.
|
||||
|
||||
After we write the code responsible for how, we run the unit test(s) and see if the code we just wrote fulfills the expected behavior. If it does, we're done. Time to move on to fulfilling the next expectation. If it doesn't, we continue transforming the code until it succeeds in passing the test.
|
||||
|
||||
If we choose not to do TDD, but write code first and later write the unit test, we miss the opportunity to separate _what_ from _how_. In other words, we write the code while simultaneously taking care of what we expect the code to do _and_ how to structure the code to do it correctly.
|
||||
|
||||
As such, writing unit tests after we write code is considered harmful.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/2/automate-unit-tests
|
||||
|
||||
作者:[Alex Bunardzic][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/alex-bunardzic
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/innovation_lightbulb_gears_devops_ansible.png?itok=TSbmp3_M (gears and lightbulb to represent innovation)
|
||||
[2]: https://opensource.com/article/20/1/test-driven-development
|
||||
@@ -1,124 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Getting started with OpenTaxSolver)
|
||||
[#]: via: (https://opensource.com/article/20/2/do-your-taxes-open-source-way)
|
||||
[#]: author: (Jessica Cherry https://opensource.com/users/jrepka)
|
||||
|
||||
Getting started with OpenTaxSolver
|
||||
======
|
||||
If you're a United States citizen, learn how to do your own state tax
|
||||
returns with OpenTaxSolver.
|
||||
![A document flying away][1]
|
||||
|
||||
OpenTaxSolver is an open source application for US taxpayers to calculate their state and federal income tax returns. Before I get into the software, I want to share some of the information I learned when researching this article. I spent about five hours a day for a week looking into open source options for doing your taxes, and I learned about a lot more than just tax software.
|
||||
|
||||
The Internal Revenue Service's (IRS's) [Use of federal tax information (FTI) in open source software][2] webpage offers a large amount of information, and it's especially relevant to anyone who may want to start their own open source tax software project. To hit the finer points:
|
||||
|
||||
* Federal tax information (FTI) can be used in any open source software
|
||||
* Software creators mush follow all security laws and compliance requirements
|
||||
* Any such software must be supported either by a vendor or a community
|
||||
* The software must be approved by the federal government
|
||||
|
||||
|
||||
|
||||
One other reason researching this topic was rather difficult (but ultimately rewarding) is that, by federal law, the major tax software companies are required to provide their services for free to any person earning under $69,000 per year. About 70% of Americans fit into this category, and if you are one of them, you can check the IRS's [Free File][3] webpage for links to free filing software from well-known companies. (The IRS reminds you that "you are responsible for determining your eligibility for one of the Free File Online offers.")
|
||||
|
||||
Please share this information broadly—knowledge is power, and not everyone can (or wants to) use open source software to do their taxes for reasons including:
|
||||
|
||||
* Lack of computer or software access
|
||||
* Low computer competence
|
||||
* Age or disability
|
||||
* Discomfort with doing taxes
|
||||
|
||||
|
||||
|
||||
If you don't fall into any of these categories and want to do your taxes the open source way, continue reading to learn about OpenTaxSolver.
|
||||
|
||||
### About OpenTaxSolver
|
||||
|
||||
[OpenTaxSolver][4] is meant to be used with the IRS's [tax booklet][5], which is published yearly. This booklet provides detailed information for doing your taxes, such as rules around tax credits and write-offs.
|
||||
|
||||
OpenTaxSolver cuts down on tax calculations when you fill out your tax forms and simplifies the hardest part of doing your taxes: the math. You still have to fill in your data and turn the paperwork in, but using the software means you can do it in about half of the time. Since OpenTaxSolver is running in beta, you have to double-check all of your number entries and information against the official IRS tax booklet after you use the software.
|
||||
|
||||
### Download and install OpenTaxSolver
|
||||
|
||||
First, [download the software][6]. There are versions for Linux, Windows, and macOS. If you're using one of the latter two, refer to the download page for installation instructions. I'm using my go-to operating system, Ubuntu Linux, which I installed by:
|
||||
|
||||
1. Downloading the TGZ file from the website
|
||||
2. Extracting it to my desktop (but you can choose any location on your computer)
|
||||
3. Clicking on **Run_taxsolve_GUI**
|
||||
|
||||
|
||||
|
||||
![OpenTaxSolver installation][7]
|
||||
|
||||
### Enter your tax data
|
||||
|
||||
I'll walk through this example using random numbers (for obvious reasons). This walkthrough will explain how do federal taxes with OpenTaxSolver, but if you have to pay state taxes, do that before you begin your federal return.
|
||||
|
||||
To do the common Federal 1040 tax return, select **US 1040**, click **Start New Return**, and start answering some basic questions about your tax situation. For this example, I selected the following itemized deductions: mortgage interest, donations, and some random itemizable write-offs. If you don't know what these are or what may apply to you, head over to the IRS website or google "itemizable write-offs."
|
||||
|
||||
Next, begin entering the data from your tax documents.
|
||||
|
||||
![OpenTaxSolver][8]
|
||||
|
||||
![OpenTaxSolver][9]
|
||||
|
||||
After you finish entering all your data and filling out the entire form, save it by clicking the **Save** button, and then click **Compute Tax** on the bottom of the screen.
|
||||
|
||||
![OpenTaxSolver][10]
|
||||
|
||||
### Check your return and file your taxes
|
||||
|
||||
If you made any mistakes (such as mistyping something or putting an incorrect value in any field), it will show an error on the bottom of the preview after the computation finishes.
|
||||
|
||||
![OpenTaxSolver preview][11]
|
||||
|
||||
The preview also reports your marginal tax rate and what percentage of your income you are paying in taxes.
|
||||
|
||||
![OpenTaxSolver preview][12]
|
||||
|
||||
After you review the information in the preview, make any corrections, and finish your return, click **Fill-out PDF Forms**, and it will provide printable tax forms with all of your information filled in.
|
||||
|
||||
![Tax return][13]
|
||||
|
||||
If you entered your name, address, and social security number when entering your data, all of that information will also appear in the right places on the form. Double-check everything, print it, and mail your tax return to the IRS.
|
||||
|
||||
### Final notes
|
||||
|
||||
OpenTaxSolver gives you the opportunity to file your own federal and state taxes. As always, with any federal related tax information (or federal anything, for that matter), always double-check and use due diligence. I found this software very useful for expanding my knowledge about my taxes.
|
||||
|
||||
The OpenTaxSolver website includes a request for contributors, so if you want to start contributing to an open source project that helps everyone, this is one I'd definitely suggest.
|
||||
|
||||
And if you're someone who likes to wait until the last minute to pay your taxes, this [clock][14] tells you how much time you have until your taxes are due.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/2/do-your-taxes-open-source-way
|
||||
|
||||
作者:[Jessica Cherry][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jrepka
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/osdc_odf_1109ay.png?itok=4CqrPAjt (A document flying away)
|
||||
[2]: https://www.irs.gov/privacy-disclosure/use-of-federal-tax-information-fti-in-open-source-software
|
||||
[3]: https://apps.irs.gov/app/freeFile/
|
||||
[4]: http://opentaxsolver.sourceforge.net/index.html
|
||||
[5]: https://www.irs.gov/pub/irs-pdf/i1040gi.pdf
|
||||
[6]: http://opentaxsolver.sourceforge.net/download2019.html?button=+Download+OTS+
|
||||
[7]: https://opensource.com/sites/default/files/uploads/tax2.png (OpenTaxSolver installation)
|
||||
[8]: https://opensource.com/sites/default/files/uploads/tax1.png (OpenTaxSolver)
|
||||
[9]: https://opensource.com/sites/default/files/uploads/tax7.png (OpenTaxSolver)
|
||||
[10]: https://opensource.com/sites/default/files/uploads/tax6.png (OpenTaxSolver)
|
||||
[11]: https://opensource.com/sites/default/files/uploads/tax3.png (OpenTaxSolver preview)
|
||||
[12]: https://opensource.com/sites/default/files/uploads/tax4.png (OpenTaxSolver preview)
|
||||
[13]: https://opensource.com/sites/default/files/uploads/tax5.png (Tax return)
|
||||
[14]: https://countdown.onlineclock.net/countdowns/taxes/
|
||||
@@ -1,229 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to embed Twine stories in WordPress)
|
||||
[#]: via: (https://opensource.com/article/20/2/embed-twine-wordpress)
|
||||
[#]: author: (Roman Lukš https://opensource.com/users/romanluks)
|
||||
|
||||
How to embed Twine stories in WordPress
|
||||
======
|
||||
Share your Twine 2 interactive stories on your WordPress site with the
|
||||
Embed Twine plugin.
|
||||
![Person drinking a hat drink at the computer][1]
|
||||
|
||||
From the very beginning, I wanted the "About me" page on my WordPress website [romanluks.eu][2] to be interactive.
|
||||
|
||||
At first, I experimented with Dart, a programming language developed by Google that transcompiles into JavaScript. I killed the project when I realized I was making a game instead of an "About me" page.
|
||||
|
||||
A bit later, I discovered [Twine][3], an open source tool for creating interactive stories. It reminded me of the gamebooks I loved as a kid. It's so easy to create interconnected pieces of text in Twine, and it's ideal for the interview-like format I was aiming for. Because Twine publishes directly to HTML, you can do a lot of interesting things with it—including creating [interactive fiction][4] and [adventure games][5] or publishing stories on a blog or website.
|
||||
|
||||
### Early struggles
|
||||
|
||||
I created my "About me" page in Twine and tried to paste it into my WordPress page.
|
||||
|
||||
"No, can do," said WordPress and Twine.
|
||||
|
||||
You see, a Twine story exported from Twine is just a webpage (i.e., a file in HTML format). However, it not only includes HTML but JavaScript code, as well. And somehow it doesn't work when you simply try to copy-paste the contents. I tried copy-pasting just the body of the Twine story page without success.
|
||||
|
||||
I thought, "I guess I need to add that JavaScript code separately," and I tried custom fields.
|
||||
|
||||
Nope.
|
||||
|
||||
I took a break from my investigation. I just uploaded my "About me" Twine story via FTP and linked to it from my website's menu. People could visit it and interact with the story, however, there was no menu, and it didn't feel like a part of my website. I had made a trap for myself. It made me realize I really _really_ wanted my "About me" included directly on my website.
|
||||
|
||||
### DIY embed
|
||||
|
||||
I took a stab at the problem and came up with [this solution][6].
|
||||
|
||||
It worked. It wasn't perfect, but it worked.
|
||||
|
||||
But it wasn't _perfect_. Is there a better way? There is bound to be a better way…
|
||||
|
||||
It cost me a couple of pulled hairs, but I managed to get a [responsive iframe and autoscroll][7].
|
||||
|
||||
It was way better. I was proud of myself and shared it on [Reddit][8].
|
||||
|
||||
### The road to Embed Twine
|
||||
|
||||
Suddenly, an idea! What if, instead of following my tutorial, people could use a WordPress plugin?
|
||||
|
||||
They would only have to give the plugin a Twine story, and it would take care of the rest. Hassle-free. No need to copy-paste any JavaScript code.
|
||||
|
||||
Wouldn't that be glorious?!?
|
||||
|
||||
I had no idea how WordPress plugins work. I only knew they are written in PHP. A while back, I had part-time work as a PHP developer, and I remembered the basics.
|
||||
|
||||
### Containers and WordPress
|
||||
|
||||
I mentioned my idea to a friend, and he suggested I could use containers as my WordPress development environment.
|
||||
|
||||
In the past, I'd always used [XAMPP][9], but I wanted to try containers for a while.
|
||||
|
||||
No problem, I thought! I'll learn containers while I learn how to make a WordPress plugin and revive my PHP skills. That should be sufficiently stimulating.
|
||||
|
||||
And it was.
|
||||
|
||||
I can't recall how many times I stopped, removed, and rebuilt my containers. I had to use the command line. And the file permissions are painful.
|
||||
|
||||
Oh boy! It was like playing a game that you enjoy playing even though it makes you fairly angry. It was challenging but rewarding.
|
||||
|
||||
I found out that it's very easy to create a simple WordPress plugin:
|
||||
|
||||
* Write the source code
|
||||
* Save it in the WP plugin directory
|
||||
* Test it
|
||||
* Repeat
|
||||
|
||||
|
||||
|
||||
Containers make it easy to use a specific environment and are easy to clean up when you screw up and need to start over.
|
||||
|
||||
Using Git saved me from accidentally wiping out my entire codebase. I used [Sourcetree][10] as my Git user interface. Initially, I wrote my code in [Notepad++][11], but when I divided my code into multiple files, I switched to [Atom][12]. It's such a cool editor for geeks. Using it feels like the code writes itself.
|
||||
|
||||
### Intermission
|
||||
|
||||
So what do we know so far?
|
||||
|
||||
* I wanted an interactive "About me" page
|
||||
* I created an "About me" story in Twine
|
||||
* Twine exports webpages (as HTML files with JavaScript included)
|
||||
* WP plugins are easy to make
|
||||
* Containers are awesome
|
||||
|
||||
|
||||
|
||||
### Embed Twine is born
|
||||
|
||||
I wanted an easy way to embed Twine stories into WordPress. So, I used the power of software development, fooled around with containers, wrote a bit of PHP code, and published the result as a WordPress plugin called [Embed Twine][13].
|
||||
|
||||
### Install the plugin
|
||||
|
||||
1. Upload the plugin [files][14] to the **/wp-content/plugins/plugin-name** directory, or install the plugin through the WordPress Plugins screen.
|
||||
2. Activate the plugin through the Plugins screen in WordPress.
|
||||
|
||||
|
||||
|
||||
### Use the plugin
|
||||
|
||||
After you've installed the Embed Twine plugin and created a Twine 2 story, embed it in your WordPress site:
|
||||
|
||||
1. Export your Twine 2 story into an HTML file.
|
||||
2. Upload it via the plugin's interface.
|
||||
3. Insert the shortcode into the page or post.
|
||||
4. Enjoy your embedded story.
|
||||
|
||||
|
||||
|
||||
The plugin also provides autoscroll functionality to make it easy for users to navigate through your stories.
|
||||
|
||||
### Configure the plugin
|
||||
|
||||
The plugin is configurable via shortcode parameters. To use the shortcode, simply put **[embed_twine]** into your post.
|
||||
|
||||
You can use additional parameters in the format **[embed_twine story="Story" aheight=112 autoscroll=true ascroll=100]** as follows:
|
||||
|
||||
* **story:** Specify the story name (the filename without an extension).
|
||||
* If the story parameter is omitted, it defaults to "Story." This means there is no need to use this parameter if your Twine filename is Story.html.
|
||||
* If you upload a Twine story called MyFooBar.html, use the shortcode: **[embed_twine story="MyFooBar"]**.
|
||||
* **aheight:** Use this parameter to adjust the iframe's height. You might need to tweak **aheight** to get rid of an iframe scrollbar. The default value is 112; this value is added to the iframe height and used to set the iframe's **style.height**.
|
||||
* **autoscroll:** Autoscroll is enabled by default. You can turn it off with shortcode parameter **[embed_twine autoscroll=false]**.
|
||||
* **ascroll:** Use this to adjust the default position for autoscroll. The default value is 100; this value is subtracted from the iframe's top position and fed into JavaScript method **window.scrollTo()**.
|
||||
|
||||
|
||||
|
||||
### Known bugs
|
||||
|
||||
Currently, Twine passages that include images might report their height incorrectly, and the scrollbar might show up for these passages. Tweak the shortcode parameter **aheight** to get rid of them.
|
||||
|
||||
### The script
|
||||
|
||||
|
||||
```
|
||||
1 <?php
|
||||
2
|
||||
3 /**
|
||||
4 * Plugin Name: Embed Twine
|
||||
5 * Description: Insert Twine stories into WordPress
|
||||
6 * Version: 0.0.6
|
||||
7 * Author: Roman Luks
|
||||
8 * Author URI: <https://romanluks.eu/>
|
||||
9 * License: GPLv2 or later
|
||||
10 */
|
||||
11
|
||||
12 require_once('include/embed-twine-load-file.php');
|
||||
13 require_once('include/embed-twine-parent-page.php');
|
||||
14 require_once('include/embed-twine-process-story.php');
|
||||
15
|
||||
16 // Add plugin to WP menu
|
||||
17 function embed_twine_customplugin_menu() {
|
||||
18
|
||||
19 add_menu_page("Embed Twine", "Embed Twine","manage_options", __FILE__, "embed_twine_uploadfile");
|
||||
20 }
|
||||
21
|
||||
22 add_action("admin_menu", "embed_twine_customplugin_menu");
|
||||
23
|
||||
24 function embed_twine_uploadfile(){
|
||||
25 include "include/embed-twine-upload-file.php";
|
||||
26 }
|
||||
27
|
||||
28 // Add shortcode
|
||||
29 function embed_twine_shortcodes_init()
|
||||
30 {
|
||||
31 function embed_twine_shortcode($atts = [], $content = null)
|
||||
32 {
|
||||
33 // Attributes
|
||||
34 $atts = shortcode_atts(
|
||||
35 [array][15](
|
||||
36 'story' => 'Story',
|
||||
37 'aheight' => 112, //adjust for style.height (30) and margins of tw-story (2x41)
|
||||
38 'autoscroll' => true, //autoscroll enabled by default
|
||||
39 'ascroll' => 100, //adjust for autoscroll
|
||||
40 ),
|
||||
41 $atts,
|
||||
42 'embed_twine'
|
||||
43 );
|
||||
44
|
||||
45 $content = embed_twine_buildParentPage($atts['story'], $atts['aheight'], $atts['autoscroll'], $atts['ascroll']);
|
||||
46
|
||||
47 return $content;
|
||||
48 }
|
||||
49 add_shortcode('embed_twine', 'embed_twine_shortcode');
|
||||
50 }
|
||||
51 add_action('init', 'embed_twine_shortcodes_init');
|
||||
```
|
||||
|
||||
* * *
|
||||
|
||||
_This article is adapted from [Roman Luks' blog][16] and [Embed Twine][13] page on WordPress plugins._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/2/embed-twine-wordpress
|
||||
|
||||
作者:[Roman Lukš][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/romanluks
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/coffee_tea_laptop_computer_work_desk.png?itok=D5yMx_Dr (Person drinking a hat drink at the computer)
|
||||
[2]: https://romanluks.eu/
|
||||
[3]: https://twinery.org/
|
||||
[4]: https://opensource.com/article/18/7/twine-vs-renpy-interactive-fiction
|
||||
[5]: https://opensource.com/article/18/2/twine-gaming
|
||||
[6]: https://romanluks.eu/blog/how-to-embed-twine-on-your-wordpress-website/
|
||||
[7]: https://romanluks.eu/blog/how-to-embed-twine-on-your-wordpress-website-with-responsive-iframe-and-autoscroll/
|
||||
[8]: https://www.reddit.com/r/twinegames/comments/dtln4z/how_to_embed_twine_on_your_wordpress_website_with/
|
||||
[9]: https://en.wikipedia.org/wiki/XAMPP
|
||||
[10]: https://www.sourcetreeapp.com/
|
||||
[11]: https://notepad-plus-plus.org/
|
||||
[12]: https://atom.io/
|
||||
[13]: https://wordpress.org/plugins/embed-twine/
|
||||
[14]: https://plugins.trac.wordpress.org/browser/embed-twine/
|
||||
[15]: http://www.php.net/array
|
||||
[16]: https://romanluks.eu/blog/embed-twine-wordpress-plugin/
|
||||
@@ -1,93 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to conveniently unsubscribe from a mailing list)
|
||||
[#]: via: (https://opensource.com/article/20/2/how-unsubscribe-mailing-list)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
How to conveniently unsubscribe from a mailing list
|
||||
======
|
||||
Cut down on your email clutter by removing yourself from email lists you
|
||||
no longer need.
|
||||
![Photo by Anthony Intraversato on Unsplash][1]
|
||||
|
||||
If you're on an email discussion group long enough, at some point, you'll see an email from a list member asking to be unsubscribed. Typically, at least 10 other people on the list will respond with instructions on how to unsubscribe, and those 10 responses will be answered by 10 more people confirming or commenting on the instructions. That's a _lot_ of traffic to a mailing list just so one person can unsubscribe.
|
||||
|
||||
But unsubscribing from a list can be confusing, especially if you've gotten on the list by accident. It's frustrating to discover that you've been added to a list, and it's annoying that you have to take time out of your day to extricate yourself. This article is here to help make unsubscribing fast, easy, and graceful.
|
||||
|
||||
Never send an unsubscribe email to the same email address you use to post messages
|
||||
|
||||
### Unsubscribe by email
|
||||
|
||||
Mailing lists are controlled by mailing list software (like [GNU Mailman][2]) on a server. You probably aren't aware of the software controlling a mailing list you're on, because they're usually designed to stay out of the way and just deliver mail. But as a member of a mailing list, you actually have some user control over the software.
|
||||
|
||||
Some mailing lists allow you to unsubscribe using an automated email address. It can be a little confusing because the email address you use to unsubscribe is NOT the email address you use to send messages to the list. Essentially, you're sending a special command to the email server, telling it to take you off the list. This is a convenient method of unsubscribing because it means you don't have to compose a message or wait for anyone to take action. You speak directly to the computer sending the email, and it does exactly as it's told.
|
||||
|
||||
To unsubscribe from a list, take the email address of the list, add **-leave** just before the **@** symbol, and send a message. You can email a blank message; the computer doesn't care. The fact that you're emailing the list with the **-leave** command in front of the **@** symbol is all it needs.
|
||||
|
||||
Here's an example.
|
||||
|
||||
Say you've joined the mailing list Funny Squirrels. You send a few messages to [funnysquirrels@example.com][3] but soon find that squirrels are not as amusing as you'd hoped. To unsubscribe, you can send an email to:
|
||||
|
||||
|
||||
```
|
||||
`funnysquirrels-leave@example.com`
|
||||
```
|
||||
|
||||
You may get a final confirmation email back, and then you'll hear from the mailing list no more.
|
||||
|
||||
#### Custom email commands
|
||||
|
||||
Sometimes the administrator of a mail server changes the command for unsubscribing. Ideally, they'll include the unsubscribe email address in the footer of emails sent to the mailing list, so check for that before sending your parting email.
|
||||
|
||||
The thing to keep in mind is that an unsubscribe email _never_ goes to the actual list, meaning you should never send an unsubscribe email to the same email address you use to post messages. There's a special, separate email address reserved for the unsubscribe command.
|
||||
|
||||
### Unsubscribing by webform
|
||||
|
||||
Some mailing lists have a webform for unsubscribing, and ideally, it can be found in the footer of each mailing list message. You can navigate to the webform and opt out of your subscription.
|
||||
|
||||
This method is common for commercial mailing lists, and it's sometimes a way for them to capture any feedback you have about the list, why you're leaving, and so on. Like the automated email method, the intent is for you to maintain full control of your own subscription. You never have to wait for a human to take you off of a list; instead, you can issue commands directly to a computer.
|
||||
|
||||
![Example unsubscribe web form][4]
|
||||
|
||||
A webform may send you a final confirmation email, and after that, you should hear nothing from that mailing list ever again.
|
||||
|
||||
### Unsubscribing like a pro
|
||||
|
||||
Leaving a mailing list is a guilt-free and nonaggressive act. When you want to leave a mailing list, you should be able to find an unsubscribe email address or webform to make it automated and final.
|
||||
|
||||
If, in spite of using the methods above, you can't leave a mailing list, don't email the list. Very few people on the mailing list have control over who is subscribed, and sometimes the people who have access to the list of subscribers are not monitoring the list—they're only maintaining the server. Instead, find out what server hosts the mailing list, and contact the hosting provider to alert them of the abuse.
|
||||
|
||||
You can find the host of a mailing list by searching for the server name (the part of the email address to the _right_ of the **@** symbol) on a **whois** service. If you're running Linux, you can do this from a terminal:
|
||||
|
||||
|
||||
```
|
||||
`$ whois <example.com>`
|
||||
```
|
||||
|
||||
Otherwise, use the [Whois.net][5] website.
|
||||
|
||||
Whois provides the internet hosting provider of any email server plus the abuse and support contact information.
|
||||
|
||||
Remember: you are always free to leave a mailing list for any reason, without getting permission from anyone else. And now that you know how, you'll be able to unsubscribe like a pro!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/2/how-unsubscribe-mailing-list
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/anthony-intraversato-pt_wqgzaiu8-unsplash.jpg?itok=5bbMlgt8 (Photo by Anthony Intraversato on Unsplash)
|
||||
[2]: https://www.list.org/
|
||||
[3]: mailto:funnysquirrels@example.com
|
||||
[4]: https://opensource.com/sites/default/files/uploads/mail-webform.jpg (Example unsubscribe web form)
|
||||
[5]: http://whois.net
|
||||
@@ -1,427 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Don't like loops? Try Java Streams)
|
||||
[#]: via: (https://opensource.com/article/20/2/java-streams)
|
||||
[#]: author: (Chris Hermansen https://opensource.com/users/clhermansen)
|
||||
|
||||
Don't like loops? Try Java Streams
|
||||
======
|
||||
It's 2020 and time to learn about Java Streams.
|
||||
![Person drinking a hat drink at the computer][1]
|
||||
|
||||
In this article, I will explain how to not write loops anymore.
|
||||
|
||||
What? Whaddaya mean, no more loops?
|
||||
|
||||
Yep, that's my 2020 resolution—no more loops in Java. Understand that it's not that loops have failed me, nor have they led me astray (well, at least, I can argue that point). Really, it is that I, a Java programmer of modest abilities since 1997 or so, must finally learn about all this new [Streams][2] stuff, saying "what" I want to do and not "how" I want to do it, maybe being able to parallelize some of my computations, and all that other good stuff.
|
||||
|
||||
I'm guessing that there are other Java programmers out there who also have been programming in Java for a decent amount of time and are in the same boat. Therefore, I'm offering my experiences as a guide to "how to not write loops in Java anymore."
|
||||
|
||||
### Find a problem worth solving
|
||||
|
||||
If you're like me, then the first show-stopper you run into is "right, cool stuff, but what am I solving for, and how do I apply this?" I realized that I can spot the perfect opportunity camouflaged as _Something I've Done Before_.
|
||||
|
||||
In my case, it's sampling land cover within a specific area and coming up with an estimate and a confidence interval around that estimate for the land cover across the whole area. The specific problem involves deciding whether an area is "forested" or not, given a specific legal definition: if at least 10% of the soil is covered over by tree crowns, then the area is considered to be forested; otherwise, it's something else.
|
||||
|
||||
![Image of land cover in an area][3]
|
||||
|
||||
It's a pretty esoteric example of a recurring problem; I'll grant you. But there it is. For the ecologists and foresters out there who are accustomed to cool temperate or tropical forests, 10% might sound kind of low, but in the case of dry areas with low-growing shrubs and trees, that's a reasonable number.
|
||||
|
||||
So the basic idea is: use images to stratify the area (i.e., areas completely devoid of trees, areas of predominantly small trees spaced quite far apart, areas of predominantly small trees spaced closer together, areas of somewhat larger trees), locate some samples in those strata, send the crew out to measure the samples, analyze the results, and calculate the proportion of soil covered by tree crowns across the area. Simple, right?
|
||||
|
||||
![Survey team assessing land cover][4]
|
||||
|
||||
### What the field data looks like
|
||||
|
||||
In the current project, the samples are rectangular areas 20 meters wide by 25 meters long, so 500 square meters each. On each patch, the field crew measured each tree: its species, its height, the maximum and minimum width of its crown, and the diameter of its trunk at trunk height (nominally 30cm above the ground). This information was collected, entered into a spreadsheet, and exported to a bar separated value (BSV) file for me to analyze. It looks like this:
|
||||
|
||||
Stratum# | Sample# | Tree# | Species | Trunk diameter (cm) | Crown diameter 1 (m) | Crown diameter 2 (m) | Height (m)
|
||||
---|---|---|---|---|---|---|---
|
||||
1 | 1 | 1 | Ac | 6 | 3.6 | 4.6 | 2.4
|
||||
1 | 1 | 2 | Ac | 6 | 2.2 | 2.3 | 2.5
|
||||
1 | 1 | 3 | Ac | 16 | 2.5 | 1.7 | 2.4
|
||||
1 | 1 | 4 | Ac | 6 | 1.5 | 2.1 | 1.8
|
||||
1 | 1 | 5 | Ac | 5 | 0.9 | 1.7 | 1.7
|
||||
1 | 1 | 6 | Ac | 6 | 1.7 | 1.3 | 1.6
|
||||
1 | 1 | 7 | Ac | 5 | 1.82 | 1.32 | 1.8
|
||||
1 | 1 | 1 | Ac | 1 | 0.3 | 0.25 | 0.9
|
||||
1 | 1 | 2 | Ac | 2 | 1.2 | 1.2 | 1.7
|
||||
|
||||
The first column is the stratum number (where 1 is "predominantly small trees spaced quite far apart," 2 is "predominantly small trees spaced closer together," and 3 is "somewhat larger trees"; we didn't sample the areas "completely devoid of trees"). The second column is the sample number (there are 73 samples altogether, located in the three strata in proportion to the area of each stratum). The third column is the tree number within the sample. The fourth is the two-letter species code, the fifth the trunk diameter (in this case, 10cm above ground or exposed roots), the sixth the smallest distance across the crown, the seventh the largest distance, and the eighth the height of the tree.
|
||||
|
||||
For the purposes of this exercise, I'm only concerned with the total amount of ground covered by the tree crowns—not the species, nor the height, nor the diameter of the trunk.
|
||||
|
||||
In addition to the measurement information above, I also have the areas of the three strata, also in a BSV:
|
||||
|
||||
stratum | hectares
|
||||
---|---
|
||||
1 | 114.89
|
||||
2 | 207.72
|
||||
3 | 29.77
|
||||
|
||||
### What I want to do (not how I want to do it)
|
||||
|
||||
In keeping with one of the main design goals of Java Streams, here is "what" I want to do:
|
||||
|
||||
1. Read the stratum area BSV and save the data as a lookup table.
|
||||
2. Read the measurements from the measurement BSV file.
|
||||
3. Accumulate each measurement (tree) to calculate the total area of the sample covered by tree crowns.
|
||||
4. Accumulate the sample tree crown area values and count the number of samples to estimate the mean tree crown area coverage and standard error of the mean for each stratum.
|
||||
5. Summarize the stratum figures.
|
||||
6. Weigh the stratum means and standard errors by the stratum areas (looked up from the table created in step 1) and accumulate them to estimate the mean tree crown area coverage and standard error of the mean for the total area.
|
||||
7. Summarize the weighted figures.
|
||||
|
||||
|
||||
|
||||
Generally speaking, the way to define "what" with Java Streams is by creating a stream processing pipeline of function calls that pass over the data. So, yes, there is actually a bit of "how" that ends up creeping in… in fact, quite a bit of "how." But, it needs a very different knowledge base than the good, old fashioned loop.
|
||||
|
||||
I'll go through each of these steps in detail.
|
||||
|
||||
#### Build the stratum area table
|
||||
|
||||
The first job is to convert the stratum areas BSV file to a lookup table:
|
||||
|
||||
|
||||
```
|
||||
[String][5] fileName = "stratum_areas.bsv";
|
||||
Stream<String> inputLineStream = Files.lines(Paths.get(fileName)); // (1)
|
||||
|
||||
final Map<[Integer][6],Double> stratumAreas = // (2)
|
||||
inputLineStream // (3)
|
||||
.skip(1) // (4)
|
||||
.map(l -> l.split("\\\|")) // (5)
|
||||
.collect( // (6)
|
||||
Collectors.toMap( // (7)
|
||||
a -> [Integer][6].parseInt(a[0]), // (8)
|
||||
a -> [Double][7].parseDouble(a[1]) // (9)
|
||||
)
|
||||
);
|
||||
inputLineStream.close(); // (10)
|
||||
|
||||
[System][8].out.println("stratumAreas = " + stratumAreas); // (11)
|
||||
```
|
||||
|
||||
I'll take this a line or two at a time, where the numbers in comments following the lines above—e.g., _// (3)_— correspond to the numbers below:
|
||||
|
||||
1. java.nio.Files.lines() gives a stream of strings corresponding to lines in the file.
|
||||
2. The goal is to create the lookup table, **stratumAreas**, which is a **Map<Integer,Double>**. Therefore, I can get the **double** value area for stratum 2 as **stratumAreas.get(2)**.
|
||||
3. This is the beginning of the stream "pipeline."
|
||||
4. Skip the first line in the pipeline since it's the header line containing the column names.
|
||||
5. Use **map()** to split the **String** input line into an array of **String** fields, with the first field being the stratum # and the second being the stratum area.
|
||||
6. Use **collect()** to [materialize the results][9].
|
||||
7. The materialized result will be produced as a sequence of **Map** entries.
|
||||
8. The key of each map entry is the first element of the array in the pipeline—the **int** stratum number. By the way, this is a _Java lambda_ expression—[an anonymous function][10] that takes an argument and returns that argument converted to an **int**.
|
||||
9. The value of each map entry is the second element of the array in the pipeline—the **double** stratum area.
|
||||
10. Don't forget to close the stream (file).
|
||||
11. Print out the result, which looks like: [code]`stratumAreas = {1=114.89, 2=207.72, 3=29.77}`
|
||||
```
|
||||
### Build the measurements table and accumulate the measurements into the sample totals
|
||||
|
||||
Now that I have the stratum areas, I can start processing the main body of data—the measurements. I combine the two tasks of building the measurements table and accumulating the measurements into the sample totals since I don't have any interest in the measurement data per se.
|
||||
```
|
||||
|
||||
|
||||
fileName = "sample_data_for_testing.bsv";
|
||||
inputLineStream = Files.lines(Paths.get(fileName));
|
||||
|
||||
final Map<[Integer][6],Map<[Integer][6],Double>> sampleValues =
|
||||
inputLineStream
|
||||
.skip(1)
|
||||
.map(l -> l.split("\\\|"))
|
||||
.collect( // (1)
|
||||
Collectors.groupingBy(a -> [Integer][6].parseInt(a[0]), // (2)
|
||||
Collectors.groupingBy(b -> [Integer][6].parseInt(b[1]), // (3)
|
||||
Collectors.summingDouble( // (4)
|
||||
c -> { // (5)
|
||||
double rm = ([Double][7].parseDouble(c[5]) +
|
||||
[Double][7].parseDouble(c[6]))/4d; // (6)
|
||||
return rm*rm * [Math][11].PI / 500d; // (7)
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
inputLineStream.close();
|
||||
|
||||
[System][8].out.println("sampleValues = " + sampleValues); // (8)
|
||||
|
||||
```
|
||||
Again, a line or two or so at a time:
|
||||
|
||||
1. The first seven lines are the same in this task and the previous, except the name of this lookup table is **sampleValues**; and it is a **Map** of **Map**s.
|
||||
2. The measurement data is grouped into samples (by sample #), which are, in turn, grouped into strata (by stratum #), so I use **Collectors.groupingBy()** at the topmost level [to separate data][12] into strata, with **a[0]** here being the stratum number.
|
||||
3. I use **Collectors.groupingBy()** once more to separate data into samples, with **b[1]** here being the sample number.
|
||||
4. I use the handy **Collectors.summingDouble()** [to accumulate the data][13] for each measurement within the sample within the stratum.
|
||||
5. Again, a Java lambda or anonymous function whose argument **c** is the array of fields, where this lambda has several lines of code that are surrounded by **{** and **}** with a **return** statement just before the **}**.
|
||||
6. Calculate the mean crown radius of the measurement.
|
||||
7. Calculate the crown area of the measurement as a proportion of the total sample area and return that value as the result of the lambda.
|
||||
8. Again, similar to the previous task. The result looks like (with some numbers elided): [code]`sampleValues = {1={1=0.09083231861452731, 66=0.06088002082602869, ... 28=0.0837823490804228}, 2={65=0.14738326403381743, 2=0.16961183847374103, ... 63=0.25083064794883453}, 3={64=0.3306323635177101, 32=0.25911911184680053, ... 30=0.2642668470291564}}`
|
||||
```
|
||||
|
||||
|
||||
|
||||
This output shows the **Map** of **Map**s structure clearly—there are three entries in the top level corresponding to the strata 1, 2, and 3, and each stratum has subentries corresponding to the proportional area of the sample covered by tree crowns.
|
||||
|
||||
#### Accumulate the sample totals into the stratum means and standard errors
|
||||
|
||||
At this point, the task becomes more complex; I need to count the number of samples, sum up the sample values in preparation for calculating the sample mean, and sum up the squares of the sample values in preparation for calculating the standard error of the mean. I may as well incorporate the stratum area into this grouping of data as well, as I'll need it shortly to weigh the stratum results together.
|
||||
|
||||
So the first thing to do is create a class, **StratumAccumulator**, to handle the accumulation and provide the calculation of the interesting results. This class implements **java.util.function.DoubleConsumer**, which can be passed to **collect()** to handle accumulation:
|
||||
|
||||
|
||||
```
|
||||
class StratumAccumulator implements DoubleConsumer {
|
||||
private double ha;
|
||||
private int n;
|
||||
private double sum;
|
||||
private double ssq;
|
||||
public StratumAccumulator(double ha) { // (1)
|
||||
this.ha = ha;
|
||||
this.n = 0;
|
||||
this.sum = 0d;
|
||||
this.ssq = 0d;
|
||||
}
|
||||
public void accept(double d) { // (2)
|
||||
this.sum += d;
|
||||
this.ssq += d*d;
|
||||
this.n++;
|
||||
}
|
||||
public void combine(StratumAccumulator other) { // (3)
|
||||
this.sum += other.sum;
|
||||
this.ssq += other.ssq;
|
||||
this.n += other.n;
|
||||
}
|
||||
public double getHa() { // (4)
|
||||
return this.ha;
|
||||
}
|
||||
public int getN() { // (5)
|
||||
return this.n;
|
||||
}
|
||||
public double getMean() { // (6)
|
||||
return this.n > 0 ? this.sum / this.n : 0d;
|
||||
}
|
||||
public double getStandardError() { // (7)
|
||||
double mean = this.getMean();
|
||||
double variance = this.n > 1 ? (this.ssq - mean*mean*n)/(this.n - 1) : 0d;
|
||||
return this.n > 0 ? [Math][11].sqrt(variance/this.n) : 0d;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Line-by-line:
|
||||
|
||||
1. The constructor **StratumAccumulator(double ha)** takes an argument, the area of the stratum in hectares, which allows me to merge the stratum area lookup table into instances of this class.
|
||||
2. The **accept(double d)** method is used to accumulate the stream of double values, and I use it to:
|
||||
a. Count the number of values.
|
||||
b. Sum the values in preparation for computing the sample mean.
|
||||
c. Sum the squares of the values in preparation for computing the standard error of the mean.
|
||||
3. The **combine()** method is used to merge substreams of **StratumAccumulator**s (in case I want to process in parallel).
|
||||
4. The getter for the area of the stratum
|
||||
5. The getter for the number of samples in the stratum
|
||||
6. The getter for the mean sample value in the stratum
|
||||
7. The getter for the standard error of the mean in the stratum
|
||||
|
||||
|
||||
|
||||
Once I have this accumulator, I can use it to accumulate the sample values pertaining to each stratum:
|
||||
|
||||
|
||||
```
|
||||
final Map<[Integer][6],StratumAccumulator> stratumValues = // (1)
|
||||
sampleValues.entrySet().stream() // (2)
|
||||
.collect( // (3)
|
||||
Collectors.toMap( // (4)
|
||||
e -> e.getKey(), // (5)
|
||||
e -> e.getValue().entrySet().stream() // (6)
|
||||
.map([Map.Entry][14]::getValue) // (7)
|
||||
.collect( // (8)
|
||||
() -> new StratumAccumulator(stratumAreas.get(e.getKey())), // (9)
|
||||
StratumAccumulator::accept, // (10)
|
||||
StratumAccumulator::combine) // (11)
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
Line-by-line:
|
||||
|
||||
1. This time, I'm using the pipeline to build **stratumValues**, which is a **Map<Integer,StratumAccumulator>**, so **stratumValues.get(3)** will return the **StratumAccumulator** instance for stratum 3.
|
||||
2. Here, I'm using the **entrySet().stream()** method provided by **Map** to get a stream of (key, value) pairs; recall these are **Map**s of sample values by stratum.
|
||||
3. Again, I'm using **collect()** to gather the pipeline results by stratum…
|
||||
4. using **Collectors.toMap()** to generate a stream of **Map** entries…
|
||||
5. whose keys are the key of the incoming stream (that is, the stratum #)…
|
||||
6. and whose values are the Map of sample values, and I again use **entrySet().stream()** to convert to a stream of Map entries, one for each sample.
|
||||
7. Using **map()** to get the value of the sample **Map** entry; I'm not interested in the key by this point.
|
||||
8. Yet again, using **collect()** to accumulate the sample results into the **StratumAccumulator** instances.
|
||||
9. Telling **collect()** how to create a new **StratumAccumulator**—I need to pass the stratum area into the constructor here, so I can't just use **StratumAccumulator::new**.
|
||||
10. Telling **collect()** to use the **accept()** method of **StratumAccumulator** to accumulate the stream of sample values.
|
||||
11. Telling **collect()** to use the **combine()** method of **StratumAccumulator** to merge **StratumAccumulator** instances.
|
||||
|
||||
|
||||
|
||||
#### Summarize the stratum figures
|
||||
|
||||
Whew! After all of that, printing out the stratum figures is pretty straightforward:
|
||||
|
||||
|
||||
```
|
||||
stratumValues.entrySet().stream()
|
||||
.forEach(e -> {
|
||||
StratumAccumulator sa = e.getValue();
|
||||
int n = sa.getN();
|
||||
double se66 = sa.getStandardError();
|
||||
double t = new TDistribution(n - 1).inverseCumulativeProbability(0.975d);
|
||||
[System][8].out.printf("stratum %d n %d mean %g se66 %g t %g se95 %g ha %g\n",
|
||||
e.getKey(), n, sa.getMean(), se66, t, se66 * t, sa.getHa());
|
||||
});
|
||||
```
|
||||
|
||||
In the above, once again, I use **entrySet().stream()** to transform the **stratumValues** Map to a stream, and then apply the **forEach()** method to the stream. **ForEach()** is pretty much what it sounds like—a loop! But the business of finding the head of the stream, finding the next element, and checking to see if hits the end is all handled by Java Streams. So, I just get to say what I want to do for each record, which is basically to print it out.
|
||||
|
||||
My code looks a bit more complicated because I declare some local variables to hold some intermediate results that I use more than once—**n**, the number of samples, and **se66**, the standard error of the mean. I also calculate the inverse T value to [convert my standard error of the mean to a 95% confidence interval][15].
|
||||
|
||||
The result looks like this:
|
||||
|
||||
|
||||
```
|
||||
stratum 1 n 24 mean 0.0903355 se66 0.0107786 t 2.06866 se95 0.0222973 ha 114.890
|
||||
stratum 2 n 38 mean 0.154612 se66 0.00880498 t 2.02619 se95 0.0178406 ha 207.720
|
||||
stratum 3 n 11 mean 0.223634 se66 0.0261662 t 2.22814 se95 0.0583020 ha 29.7700
|
||||
```
|
||||
|
||||
#### Accumulate the stratum means and standard errors into the total
|
||||
|
||||
Once again, the task becomes more complex, so I create a class, **TotalAccumulator**, to handle the accumulation and provide the calculation of the interesting results. This class implements **java.util.function.Consumer<T>**, which can be passed to **collect()** to handle accumulation:
|
||||
|
||||
|
||||
```
|
||||
class TotalAccumulator implements Consumer<StratumAccumulator> {
|
||||
private double ha;
|
||||
private int n;
|
||||
private double sumWtdMeans;
|
||||
private double ssqWtdStandardErrors;
|
||||
public TotalAccumulator() {
|
||||
this.ha = 0d;
|
||||
this.n = 0;
|
||||
this.sumWtdMeans = 0d;
|
||||
this.ssqWtdStandardErrors = 0d;
|
||||
}
|
||||
public void accept(StratumAccumulator sa) {
|
||||
double saha = sa.getHa();
|
||||
double sase = sa.getStandardError();
|
||||
this.ha += saha;
|
||||
this.n += sa.getN();
|
||||
this.sumWtdMeans += saha * sa.getMean();
|
||||
this.ssqWtdStandardErrors += saha * saha * sase * sase;
|
||||
}
|
||||
public void combine(TotalAccumulator other) {
|
||||
this.ha += other.ha;
|
||||
this.n += other.n;
|
||||
this.sumWtdMeans += other.sumWtdMeans;
|
||||
this.ssqWtdStandardErrors += other.ssqWtdStandardErrors;
|
||||
}
|
||||
public double getHa() {
|
||||
return this.ha;
|
||||
}
|
||||
public int getN() {
|
||||
return this.n;
|
||||
}
|
||||
public double getMean() {
|
||||
return this.ha > 0 ? this.sumWtdMeans / this.ha : 0d;
|
||||
}
|
||||
public double getStandardError() {
|
||||
return this.ha > 0 ? [Math][11].sqrt(this.ssqWtdStandardErrors) / this.ha : 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
I'm not going to go into much detail on this, since it's structurally pretty similar to **StratumAccumulator**. Of main interest:
|
||||
|
||||
1. The constructor takes no arguments, which simplifies its use.
|
||||
2. The **accept()** method accumulates instances of **StratumAccumulator**, not **double** values, hence the use of the **Consumer<T>** interface.
|
||||
3. As for the calculations, they are assembling a weighted average of the **StratumAccumulator** instances, so they make use of the stratum areas, and the formulas might look a bit strange to anyone who's not used to stratified sampling.
|
||||
|
||||
|
||||
|
||||
As for actually carrying out the work, it's easy-peasy:
|
||||
|
||||
|
||||
```
|
||||
final TotalAccumulator totalValues =
|
||||
stratumValues.entrySet().stream()
|
||||
.map([Map.Entry][14]::getValue)
|
||||
.collect(TotalAccumulator::new, TotalAccumulator::accept, TotalAccumulator::combine);
|
||||
```
|
||||
|
||||
Same old stuff as before:
|
||||
|
||||
1. Use **entrySet().stream()** to convert the **stratumValue Map** entries to a stream.
|
||||
2. Use **map()** to replace the **Map** entries with their values—the instances of **StratumAccumulator**.
|
||||
3. Use **collect()** to apply the **TotalAccumulator** to the instances of **StratumAccumulator**.
|
||||
|
||||
|
||||
|
||||
#### Summarize the total figures
|
||||
|
||||
Getting the interesting bits out of the **TotalAccumulator** instance is also pretty straightforward:
|
||||
|
||||
|
||||
```
|
||||
int nT = totalValues.getN();
|
||||
double se66T = totalValues.getStandardError();
|
||||
double tT = new TDistribution(nT - stratumValues.size()).inverseCumulativeProbability(0.975d);
|
||||
[System][8].out.printf("total n %d mean %g se66 %g t %g se95 %g ha %g\n",
|
||||
nT, totalValues.getMean(), se66T, tT, se66T * tT, totalValues.getHa());
|
||||
```
|
||||
|
||||
Similar to the **StratumAccumulator**, I just call the relevant getters to pick out the number of samples **nT** and the standard error **se66T**. I calculate the T value **tT** (using "n – 3" here since there are three strata), and then I print the result, which looks like this:
|
||||
|
||||
|
||||
```
|
||||
`total n 73 mean 0.139487 se66 0.00664653 t 1.99444 se95 0.0132561 ha 352.380`
|
||||
```
|
||||
|
||||
### In conclusion
|
||||
|
||||
Wow, that looks like a bit of a marathon. It feels like it, too. As is often the case, there is a great deal of information about how to use Java Streams, all illustrated with toy examples, which kind of help, but not really. I found that getting this to work with a real-world (albeit very simple) example was difficult.
|
||||
|
||||
Because I've been working in [Groovy][16] a lot lately, I kept finding myself wanting to accumulate into "maps of maps of maps" rather than creating accumulator classes, but I was never able to pull that off except in the case of totaling up the measurements in the sample. So, I worked with accumulator classes instead of maps of maps, and maps of accumulator classes instead of maps of maps of maps.
|
||||
|
||||
I don't feel like any kind of master of Java Streams at this point, but I do feel I have a pretty solid understanding of **collect()**, which is deeply important, along with various methods to reformat data structures into streams and to reformat stream elements themselves. So yeah, more to learn!
|
||||
|
||||
Speaking of collect(), in the examples I presented above, we can see moving from a very simple use of this fundamental method - using the Collectors.summingDouble() accumulation method - through defining an accumulator class that extends one of the pre-defined interfaces - in this case DoubleConsumer - to defining a full-blown accumulator of our own, used to accumulate the intermediate stratum class. I was tempted - sort of - to work backward and implement fully custom accumulators for the stratum and sample accumulators, but the point of this exercise was to learn more about Java Streams, not to become an expert in one single part of it all.
|
||||
|
||||
What's your experience with Java Streams? Done anything big and complicated yet? Please share it in the comments.
|
||||
|
||||
Optimizing your Java code requires an understanding of how the different elements in Java interact...
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/2/java-streams
|
||||
|
||||
作者:[Chris Hermansen][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/clhermansen
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/coffee_tea_laptop_computer_work_desk.png?itok=D5yMx_Dr (Person drinking a hat drink at the computer)
|
||||
[2]: https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
|
||||
[3]: https://opensource.com/sites/default/files/uploads/landcover.png (Image of land cover in an area)
|
||||
[4]: https://opensource.com/sites/default/files/uploads/foresters.jpg (Survey team assessing land cover)
|
||||
[5]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string
|
||||
[6]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+integer
|
||||
[7]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+double
|
||||
[8]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+system
|
||||
[9]: https://www.baeldung.com/java-8-collectors
|
||||
[10]: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
|
||||
[11]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+math
|
||||
[12]: https://www.baeldung.com/java-groupingby-collector
|
||||
[13]: http://www.java2s.com/Tutorials/Java/java.util.stream/Collectors/Collectors.summingDouble_ToDoubleFunction_super_T_mapper_.htm
|
||||
[14]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+map.entry
|
||||
[15]: https://en.wikipedia.org/wiki/Standard_error
|
||||
[16]: http://groovy-lang.org/
|
||||
@@ -1,414 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (The Zen of Go)
|
||||
[#]: via: (https://dave.cheney.net/2020/02/23/the-zen-of-go)
|
||||
[#]: author: (Dave Cheney https://dave.cheney.net/author/davecheney)
|
||||
|
||||
The Zen of Go
|
||||
======
|
||||
|
||||
_This article was derived from my [GopherCon Israel 2020][1] presentation. It’s also quite long. If you’d prefer a shorter version, head over to [the-zen-of-go.netlify.com][2]_.
|
||||
|
||||
_A recording of the presentation is available on [YouTube][3]._
|
||||
|
||||
* * *
|
||||
|
||||
### How should I write good code?
|
||||
|
||||
Something that I’ve been thinking about a lot recently, when reflecting on the body of my own work, is a common subtitle, _how should I write good code?_ Given nobody actively seeks to write _bad_ code, this leads to the question; _how do you know when you’ve written good Go code?_
|
||||
|
||||
If there’s a continuum between good and bad, how to do we know what the good parts are? What are its properties, its attributes, its hallmarks, its patterns, and its idioms?
|
||||
|
||||
### Idiomatic Go
|
||||
|
||||
![][4]
|
||||
|
||||
Which brings me to idiomatic Go. To say that something is idiomatic is to say that it follows the style of the time. If something is not idiomatic, it is not following the prevailing style. It is unfashionable.
|
||||
|
||||
More importantly, to say to someone that their code is not idiomatic does not explain _why_ it’s not idiomatic. Why is this? Like all truths, the answer is found in the dictionary.
|
||||
|
||||
> idiom (noun): a group of words established by usage as having a meaning not deducible from those of the individual words.
|
||||
|
||||
Idioms are hallmarks of shared values. Idiomatic Go is not something you learn from a book, it’s something that you acquire by being part of a community.
|
||||
|
||||
![][5]
|
||||
|
||||
My concern with the mantra of idiomatic Go is, in many ways, it can be exclusionary. It’s saying “you can’t sit with us.” After all, isn’t that what we mean when critique of someone’s work as non-idiomatic? They didn’t do It right. It doesn’t look right. It doesn’t follow the style of time.
|
||||
|
||||
I offer that idiomatic Go is not a suitable mechanism for teaching how to write good Go code because it is defined, fundamentally, by telling someone they did it wrong. Wouldn’t it be better if the advice we gave didn’t alienate the author right at the point they were most willing to accept it?
|
||||
|
||||
### Proverbs
|
||||
|
||||
Stepping away problematic idioms, what other cultural artefacts do Gophers have? Perhaps we can turn to Rob Pike’s wonderful [Go Proverbs][6]. Are these suitable teaching tools? Will these tell newcomers how to write good Go code?
|
||||
|
||||
In general, I don’t think so. This is not to dismiss Pike’s work, it is just that the Go Proverbs, like Segoe Kensaku’s original, are observations, not statements of value. Again, the dictionary comes to the rescue:
|
||||
|
||||
> proverb (noun): a short, well-known pithy saying, stating a general truth or piece of advice.
|
||||
|
||||
The goal of the Go Proverbs are to reveal a deeper truth about the design of the language, but how useful is advice like the _empty interface says nothing_ to a novice from a language that doesn’t have structural typing?
|
||||
|
||||
It’s important to recognise that, in a growing community, at any time the people learning Go far outnumber those who claim to have mastered the language. Thus proverbs are perhaps not the best teaching tool in this scenario.
|
||||
|
||||
### Engineering Values
|
||||
|
||||
Dan Luu found [an old presentation][7] by Mark Lucovsky about the engineering culture of the windows team around the windows NT-windows 2000 timeframe. The reason I mention it is Lukovsky’s description of a culture as a common way of evaluating designs and making tradeoffs.
|
||||
|
||||
![][8]
|
||||
|
||||
There are many ways of discussing culture, but with respect to an engineering culture Lucovsky’s description is apt. The central idea is _values guide decisions in an unknown design space_. The values of the NT team were; portability, reliability, security, and extensibility. Engineering values are, crudely translated, the way things are done around here.
|
||||
|
||||
### Go’s values
|
||||
|
||||
What are the explicit values of Go? What are the core beliefs or philosophy that define the way a Go programmer interprets the world? How are they promulgated? How are they taught? How are they enforced? How do they change over time?
|
||||
|
||||
How will you, as a newly minted Go programmer, inculcate the engineering values of Go? Or, how will you, a seasoned Go professional promulgate your values to a future generations? And just so we’re clear, this process of knowledge transfer is not optional. Without new blood and new ideas, our community become myopic and wither.
|
||||
|
||||
#### The values of other languages
|
||||
|
||||
To set the scene for what I’m getting at we can look to other languages we see examples of their engineering values.
|
||||
|
||||
For example, C++ (and by extension Rust) believe that a programmer _should not have to pay for a feature they do not use_. If a program does not use some computationally expensive feature of the language, then it shouldn’t be forced to shoulder the cost of that feature. This value extends from the language, to its standard library, and is used as a yardstick for judging the design of all code written in C++.
|
||||
|
||||
In Java, and Ruby, and Smalltalk, the core value that _everything is an object_ drives the design of programs around message passing, information hiding, and polymorphism. Designs that shoehorn a procedural style, or even a functional style, into these languages are considered to be wrong–or as Gophers would say, non idiomatic.
|
||||
|
||||
Turning to our own community, what are the engineering values that bind Go programmers? Discourse in our community is often fractious, so deriving a set of values from first principles would be a formidable challenge. Consensus is critical, but exponentially more difficult as the number of contributors to the discussion increases. But what if someone had done the hard work for us.
|
||||
|
||||
### The Zen of ~~Python~~ Go
|
||||
|
||||
Several decades ago Tim Peters sat down and penned _[PEP-20][9]_, the Zen of Python. Peters’ attempted to document the engineering values that he saw Guido van Rossum apply in his role as BDFL for Python.
|
||||
|
||||
For the remainder of this article, I’m going to look towards the Zen of Python and ask, is there anything that can inform the engineering values of Go programmers?
|
||||
|
||||
### A good package starts with a good name
|
||||
|
||||
Let’s start with something spicy,
|
||||
|
||||
> “Namespaces are one honking great idea–let’s do more of those!”
|
||||
|
||||
The Zen of Python, Item 19
|
||||
|
||||
This is pretty unequivocal, Python programmers should use namespaces. Lots of them.
|
||||
|
||||
In Go parlance a namespace is a package. I doubt there is any question that grouping things into packages is good for design and potentially reuse. But there might be some confusion, especially if you’re coming with a decade of experience in another language, about the right way to do this.
|
||||
|
||||
In Go each package should have a purpose, and the best way to know a package’s purpose is by its name—a noun. A package’s name describes what it provides. So too reinterpret Peters’ words, every Go package should have a single purpose.
|
||||
|
||||
This is not a new idea, [I’ve been saying this a while][10], but why should you do this rather than approach where packages are used for fine grained taxonomy? Why, because change.
|
||||
|
||||
> “Design is the art of arranging code to work today, and be changeable forever.”
|
||||
|
||||
Sandi Metz
|
||||
|
||||
Change is the name of the game we’re in. What we do as programmers is manage change. When we do that well we call it design, or architecture. When we do it badly we call it technical debt, or legacy code.
|
||||
|
||||
If you are writing a program that works perfectly, one time, for one fixed set of inputs then nobody cares if the code is good or bad because ultimately the output of the program is all the business cares about.
|
||||
|
||||
But this is _never_ true. Software has bugs, requirements change, inputs change, and very few programs are written solely to be executed once, thus your program _will_ change over time. Maybe it’s you who’ll be tasked with this, more likely it will be someone else, but someone has to change that code. Someone has to maintain that code.
|
||||
|
||||
So, how can we make it easy to for programs to change? Interfaces everywhere? Make everything mockable? Pernicious dependency injection? Well, maybe, for some classes of programs, but not many, those techniques will be useful. However, for the majority of programs, designing something to be flexible up front is over engineering.
|
||||
|
||||
What if, instead, we take a position that rather than enhancing components, we replace them. Then the best way to know when something needs to be replaced, is when it doesn’t do what it says on the tin.
|
||||
|
||||
A good package starts with choosing a good name. Think of your package’s name as an elevator pitch, using just one word, to describe what it provides. When the name no longer matches the requirement, find a replacement.
|
||||
|
||||
### Simplicity matters
|
||||
|
||||
> “Simple is better than complex.”
|
||||
|
||||
The Zen of Python, Item 3
|
||||
|
||||
PEP-20 says simple is better than complex, I couldn’t agree more. A couple of years ago I made this tweet;
|
||||
|
||||
> Most programming languages start out aiming to be simple, but end up just settling for being powerful.
|
||||
>
|
||||
> — Dave Cheney (@davecheney) [December 2, 2014][11]
|
||||
|
||||
My observation, at least at the time, was that I couldn’t think of a language introduced in my life time that didn’t purport to be simple. Each new language offered as a justification, and an enticement, their inherent simplicity. But as I researched, I found that simplicity was not a core value of the many of the languages considered Go’s contemporaries. [1][12] Maybe this is just a cheap shot, but could it be that either these languages aren’t simple, or they don’t _think_ of themselves as being simple. They don’t consider simplicity to be a core value.
|
||||
|
||||
Call me old fashioned, but when did being simple fall out of style? Why does the commercial software development industry continually, gleefully, forget this fundamental truth?
|
||||
|
||||
> “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.”
|
||||
|
||||
C. A. R. Hoare, The Emperor’s Old Clothes, 1980 Turing Award Lecture
|
||||
|
||||
Simple does not mean easy, we know that. Often it is more work to make something simple to use, than easy to build.
|
||||
|
||||
> “Simplicity is prerequisite for reliability.”
|
||||
|
||||
Edsger W Dijkstra, EWD498, 18 June 1975
|
||||
|
||||
Why should we strive for simplicity? Why is important that Go programs be simple? Simple doesn’t mean crude, it means readable and maintainable. Simple doesn’t mean unsophisticated, it means reliable, relatable, and understandable.
|
||||
|
||||
> “Controlling complexity is the essence of computer programming.”
|
||||
|
||||
Brian W. Kernighan, _Software Tools_ (1976)
|
||||
|
||||
Whether Python abides by its mantra of simplicity is a matter for debate, but Go holds simplicity as a core value. I think that we can all agree that when it comes to Go, simple code is preferable to clever code.
|
||||
|
||||
### Avoid package level state
|
||||
|
||||
> “Explicit is better than implicit.”
|
||||
|
||||
_The Zen of Python, Item_ 2
|
||||
|
||||
This is a place where I think Peters’ was more aspirational than factual. Many things in Python are not explicit; decorators, dunder methods, and so on. Without doubt they are powerful, there’s a reason those features exists. Each feature is something someone cared enough about to do the work to implement it, especially the complicated ones. But heavy use of those features makes is harder for the reader to predict the cost of an operation.
|
||||
|
||||
The good news is we have a choice, as Go programmers, to choose to make our code explicit. Explicit could mean many things, perhaps you may be thinking explicit is just a nice way of saying bureaucratic and long winded, but that’s a superficial interpretation. It’s a misnomer to focus only on the syntax on the page, to fret about line lengths and DRYing up expressions. The more valuable, in my opinon, place to be explicit are to do with coupling and with state.
|
||||
|
||||
Coupling is a measure of the amount one thing depends on another. If two things are tightly coupled, they move together. An action that affects one is directly reflected in another. Imagine a train, each carriage joined–ironically the correct word is coupled–together; where the engine goes, the carriages follow.
|
||||
|
||||
Another way to describe coupling is the word cohesion. Cohesion measures how well two things naturally belong together. We talk about a cohesive argument, or a cohesive team; all their parts fit together as if they were designed that way.
|
||||
|
||||
Why does coupling matter? Because just like trains, when you need to change a piece of code, all the code that is tightly coupled to it must change. A prime example, someone release a new version of their API and now your code doesn’t compile.
|
||||
|
||||
APIs are an unavoidable source of coupling but there are more insidious forms of coupling. Clearly everyone knows that if an API’s signature changes the data passing into and out of that call changes. It’s right there in the signature of the function; I take values of these types and return values of other types. But what if the API passed data another way? What if every time you called this API the result was based on the previous time you called that API even though you didn’t change your parameters.
|
||||
|
||||
This is state, and management of state is _the_ problem in computer science.
|
||||
|
||||
```
|
||||
package counter
|
||||
|
||||
var count int
|
||||
|
||||
func Increment(n int) int {
|
||||
count += n
|
||||
return count
|
||||
}
|
||||
```
|
||||
|
||||
Suppose we have this simple `counter` package. You can call `Increment` to increment the counter, you can even get the value back if you `Increment` with a value of zero.
|
||||
|
||||
Suppose you had to test this code, how would you reset the counter after each test? Suppose you wanted to run those tests in parallel, could you do it? Now suppose that you wanted to count more than one thing per program, could you do it?
|
||||
|
||||
No, of course not. Clearly the answer is to encapsulate the `count` variable in a type.
|
||||
|
||||
```
|
||||
package counter
|
||||
|
||||
type Counter struct {
|
||||
count int
|
||||
}
|
||||
|
||||
func (c *Counter) Increment(n int) int {
|
||||
c.count += n
|
||||
return c.count
|
||||
}
|
||||
```
|
||||
|
||||
Now imagine that this problem isn’t restricted to just counters, but your applications main business logic. Can you test it in isolation? Can you test it in parallel? Can you use more than one instance at a time? If the answer those question is _no_, the reason is package level state.
|
||||
|
||||
Avoid package level state. Reduce coupling and spooky action at a distance by providing the dependencies a type needs as fields on that type rather than using package variables.
|
||||
|
||||
### Plan for failure, not success
|
||||
|
||||
> “Errors should never pass silently.”
|
||||
|
||||
_The Zen of Python, Item 1_0
|
||||
|
||||
It’s been said of languages that favour exception handling follow the Samurai principle; _return victorious or not at all_. In exception based languages functions only return valid results. If they don’t succeed then control flow takes an entirely different path.
|
||||
|
||||
Unchecked exceptions are clearly an unsafe model to program in. How can you possibly write code that is robust in the presence of errors when you don’t know which statements could throw an exception? Java tried to make exceptions safer by introducing the notion of a checked exception which, to the best of my knowledge, has not been repeated in another mainstream language. There are plenty of languages which use exceptions but they all, with the singular exception of Java, do so in the unchecked variety.
|
||||
|
||||
Obviously Go chose a different path. Go programmers believe that robust programs are composed from pieces that handle the failure cases _before_ they handle the happy path. In the space that Go was designed for; server programs, multi threaded programs, programs that handle input over the network, dealing with unexpected data, timeouts, connection failures and corrupted data must be front and centre of the programmer’s mind if they are to produce robust programs.
|
||||
|
||||
> “I think that error handling should be explicit, this should be a core value of the language.”
|
||||
|
||||
Peter Bourgon, [GoTime #91][13]
|
||||
|
||||
I want to echo Peter’s assertion, as it was the impetus for this article. I think so much of the success of Go is due to the explicit way errors are handled. Go programmers thinks about the failure case first. We solve the “what if…” case first. This leads to programs where failures are handled at the point of writing, rather than the point they occur in production.
|
||||
|
||||
The verbosity of
|
||||
|
||||
```
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
is outweighed by the value of deliberately handling each failure condition at the point at which they occur. Key to this is the cultural value of handling each and every error explicitly.
|
||||
|
||||
### Return early rather than nesting deeply
|
||||
|
||||
> “Flat is better than nested.”
|
||||
|
||||
The Zen of Python, Item 5
|
||||
|
||||
This is sage advice coming from a language where indentation is the primary form of control flow. How can we interpret this advice in terms of Go? `gofmt` controls the overall whitespace of a Go program so there’s not thing doing there.
|
||||
|
||||
I wrote earlier about package names, and there is probably some advice here about avoiding a complicated package hierarchy. In my experience the more a programmer tries to subdivide and taxonimise their Go codebase the more they risk hitting the dead end that is package import loops.
|
||||
|
||||
I think the best application of item 5’s advice is the control flow _within_ a function. Simply put, avoid control flow that requires deep indentation.
|
||||
|
||||
> “Line of sight is a straight line along which an observer has unobstructed vision.”
|
||||
|
||||
May Ryer, [Code: Align the happy path to the left edge][14]
|
||||
|
||||
Mat Ryer describes this idea as line of sight coding. Light of sight coding means things like:
|
||||
|
||||
* Using guard clauses to return early if a precondition is not met.
|
||||
* Placing the successful return statement at the end of the function rather than inside a conditional block.
|
||||
* Reducing the overall indentation level of the function by extracting functions and methods.
|
||||
|
||||
|
||||
|
||||
Key to this advice is the thing that you care about, the thing that the function does, is never in danger of sliding out of sight to the right of your screen. This style has a bonus side effect that you’ll avoid pointless arguments about line lengths on your team.
|
||||
|
||||
Every time you indent you add another precondition to the programmers stack, consuming one of their 7 ±2 short term memory slots. Rather than nesting deeply, keep the successful path of the function close to the left hand side of your screen.
|
||||
|
||||
### If you think it’s slow, prove it with a benchmark
|
||||
|
||||
> “In the face of ambiguity, refuse the temptation to guess.”
|
||||
|
||||
The Zen of Python, Item 12
|
||||
|
||||
Programming is based on mathematics and logic, two concepts which rarely involve the element of chance. But there are many things we, as programmers, guess about every day. What does this variable do? What does this parameter do? What happens if I pass `nil` here? What happens if I call `Register` twice? There’s actually a lot of guesswork in modern programming, especially when it comes to using libraries you didn’t write.
|
||||
|
||||
> “APIs should be easy to use and hard to misuse.”
|
||||
|
||||
Josh Bloch
|
||||
|
||||
One of the best ways I know to help a programmer avoid having to guess is to, when building an API, [focus on the default use case][15]. Make it as easy as you can for the caller to do the most common thing. However, I’ve written and talked a lot about API design in the past, so instead my interpretation of item 12 is; _don’t guess about performance_.
|
||||
|
||||
Despite how you may feel about Knuth’s advice, one of the drivers of Go’s success is its efficient execution. You can write efficient programs in Go and thus people _will_ choose Go because of this. There are a lot of misconceptions about performance, so my request is, when you’re looking to performance tune your code or you’re facing some dogmatic advice like defer is slow, CGO is expensive, or always use atomics not mutexes, don’t guess.
|
||||
|
||||
Don’t complicate your code because of outdated dogma, and, if you think something is slow, first prove it with a benchmark. Go has excellent benchmarking and profiling tools that come in the distribution for free. Use them to find your bottlenecks.
|
||||
|
||||
### Before you launch a goroutine, know when it will stop
|
||||
|
||||
At this point I think I think I’ve mined the valuable points from PEP-20 and possibly stretched its reinterpretation beyond the point of good taste. I think that’s fine, because although this was a useful rhetorical device, ultimately we are talking about two different languages.
|
||||
|
||||
> “You type g o, a space, and then a function call. Three keystrokes, you can’t make it much shorter than that. Three keystrokes and you’ve just started a sub process.”
|
||||
|
||||
Rob Pike, [Simplicity is Complicated][16], dotGo 2015
|
||||
|
||||
The next two suggestions I’ll dedicate to goroutines. Goroutines are the signature feature of the language, our answer for first class concurrency. They are so easy to use, just put the word `go` in front of the statement and you’ve launched that function asynchronously. It’s so simple, no threads, no stack sizes, no thread pool executors, no ID’s, no tracking completion status.
|
||||
|
||||
Goroutines are cheap. Because of the runtime’s ability to multiplex goroutines onto a small pool of threads (which you don’t have to manage), hundreds of thousands, millions of goroutines are easily accommodated. This opens up designs that would be not be practical under competing concurrency models like threads or evented callbacks.
|
||||
|
||||
But as cheap as goroutines are, they’re not free. At a minimum there’s a few kilobytes for their stack, which, when you’re getting up into the 10^6 goroutines, does start to add up. This is not to say you shouldn’t use millions of goroutines if that is what the design calls for, but when you do, it’s critical that you keep track of them because 10^6 of anything can consume a non trivial amount of resources in aggregate.
|
||||
|
||||
Goroutines are the key to resource ownership in Go. To be useful a goroutine has to do something, and that means it almost always holds reference to, or ownership of, a resource; a lock, a network connection, a buffer with data, the sending end of a channel. While that goroutine is alive, the lock is held, the network connection remains open, the buffer retained and the receivers of the channel will continue to wait for more data.
|
||||
|
||||
The simplest way to free those resources is to tie them to the lifetime of the goroutine–when the goroutine exits, the resource has been freed. So while it’s near trivial to start a goroutine, before you write those three letters, g o and a space, make sure you have an answer to these questions:
|
||||
|
||||
* **Under what condition will a goroutine stop?** Go doesn’t have a way to tell a goroutine to exit. There is no stop or kill function, for good reason. If we cannot command a goroutine to stop, we must instead ask it, politely. Almost always this comes down to a channel operation. Range loops over a channel exit when the channel is closed. A channel will become selectable if it is closed. The signal from one goroutine to another is best expressed as a closed channel.
|
||||
* **What is required for that condition to arise?** If channels are both the vehicle to communicate between goroutines and the mechanism for them to signal completion, the next question to the programmer becomes, who will close the channel, when will that happen?
|
||||
* **What signal will you use to know the goroutine has stopped?** When you signal a goroutine to stop, that stopping will happen at some time in the future relative to the goroutine’s frame of reference. It might happen quickly in terms of human perception, but computers execute billions of instructions every second, and from the point of view of each goroutine, their execution of instructions is unsynchronised. The solution is often to use a channel to signal back or a waitgroup where a fan in approach is needed.
|
||||
|
||||
|
||||
|
||||
### Leave concurrency to the caller
|
||||
|
||||
It is likely that in any serious Go program you write there will be concurrency involved. This raises the problem, many of the libraries and code that we write fall into this a one goroutine per connection, or worker pattern. How will you manage the lifetime of those goroutines?
|
||||
|
||||
`net/http` is a prime example. Shutting down the server owning the listening socket is relatively straight forward, but what about a goroutines spawned from that accepting socket? `net/http` does provide a context object inside the request object which can be used to signal–to code that is listening–that the request should be canceled, thereby terminating the goroutine, however it is less clear how to know when all of these things have been done. It’s one thing to call `context.Cancel`, its another to know that the cancellation has completed.[2][17]
|
||||
|
||||
The point I want to make about `net/http` is that its a counter example to good practice. Because each connection is handled by a goroutine spawned inside the `net/http.Server` type, the program, living outside the `net/http` package, does not have an ability to control the goroutines spawned for the accepting socket.
|
||||
|
||||
This is an area of design that is still evolving, with efforts like go-kit’s `run.Group` and the Go team’s [`ErrGroup`][18] which provide a framework to execute, cancel and wait on functions run asynchronously.
|
||||
|
||||
The bigger design maxim here is for library writers, or anyone writing code that could be run asynchronously, leave the responsibility of starting to goroutine to your caller. Let the caller choose how they want to start, track, and wait on your functions execution.
|
||||
|
||||
### Write tests to lock in the behaviour of your package’s API
|
||||
|
||||
Perhaps you were hoping to read an article from me where I didn’t rant about testing. Sadly, today is not that day.
|
||||
|
||||
Your tests are the contract about what your software does and does not do. Unit tests at the package level should lock in the behaviour of the package’s API. They describe, in code, what the package promises to do. If there is a unit test for each input permutation, you have defined the contract for what the code will do _in code_, not documentation.
|
||||
|
||||
This is a contract you can assert as simply as typing `go test`. At any stage, you can _know_ with a high degree of confidence, that the behaviour people relied on before your change continues to function after your change.
|
||||
|
||||
Tests lock in api behaviour. Any change that adds, modifies or removes a public api must include changes to its tests.
|
||||
|
||||
### Moderation is a virtue
|
||||
|
||||
Go is a simple language, only 25 keywords. In some ways this makes the features that are built into the language stand out. Equally these are the features that the language sells itself on, lightweight concurrency, structural typing.
|
||||
|
||||
I think all of us have experienced the confusion that comes from trying to use all of Go’s features at once. Who was so excited to use channels that they used them as much as they could, as often as they could? Personally for me I found the result was hard to test, fragile, and ultimately overcomplicated. Am I alone?
|
||||
|
||||
I had the same experience with goroutines, attempting to break the work into tiny units I created a hard to manage hurd of Goroutines and ultimately missed the observation that most of my goroutines were always blocked waiting for their predecessor– the code was ultimately sequential and I had added a lot of complexity for little real world benefit. Who has experienced something like this?
|
||||
|
||||
I had the same experience with embedding. Initially I mistook it for inheritance. Then later I recreated the fragile base class problem by composing complicated types, which already had several responsibilities, into more complicated mega types.
|
||||
|
||||
This is potentially the least actionable piece of advice, but one I think is important enough to mention. The advice is always the same, all things in moderation, and Go’s features are no exception. If you can, don’t reach for a goroutine, or a channel, or embed a struct, anonymous functions, going overboard with packages, interfaces for everything, instead prefer simpler approach rather than the clever approach.
|
||||
|
||||
### Maintainability counts
|
||||
|
||||
I want to close with one final item from PEP-20,
|
||||
|
||||
> “Readability Counts.”
|
||||
|
||||
The Zen of Python, Item 7
|
||||
|
||||
So much has been said, about the importance of readability, not just in Go, but all programming languages. People like me who stand on stages advocating for Go use words like simplicity, readability, clarity, productivity, but ultimately they are all synonyms for one word–_maintainability_.
|
||||
|
||||
The real goal is to write maintainable code. Code that can live on after the original author. Code that can exist not just as a point in time investment, but as a foundation for future value. It’s not that readability doesn’t matter, maintainability matters _more_.
|
||||
|
||||
Go is not a language that optimises for clever one liners. Go is not a language which optimises for the least number of lines in a program. We’re not optimising for the size of the source code on disk, nor how long it takes to type the program into an editor. Rather, we want to optimise our code to be clear to the reader. Because its the reader who’s going to have to maintain this code.
|
||||
|
||||
If you’re writing a program for yourself, maybe it only has to run once, or you’re the only person who’ll ever see it, then do what ever works for you. But if this is a piece of software that more than one person will contribute to, or that will be used by people over a long enough time that requirements, features, or the environment it runs in may change, then your goal must be for your program to be maintainable. If software cannot be maintained, then it will be rewritten; and that could be the last time your company will invest in Go.
|
||||
|
||||
Can the thing you worked hard to build be maintained after you’re gone? What can you do today to make it easier for someone to maintain your code tomorrow?
|
||||
|
||||
##### [the-zen-of-go.netlify.com][2]
|
||||
|
||||
1. This part of the talk had several screenshots of the landing pages for the websites for [Ruby][19], [Swift][20], [Elm][21], [Go][22], [NodeJS][23], [Python][24], [Rust][25], highlighting how the language described itself.[][26]
|
||||
2. I tend to pick on `net/http` a lot, and this is not because it is bad, in fact it is the opposite, it is the most successful, oldest, most used API in the Go codebase. And because of that its design, evolution, and shortcoming have been thoroughly picked over. Think of this as flattery, not criticism.[][27]
|
||||
|
||||
|
||||
|
||||
#### Related posts:
|
||||
|
||||
1. [Never start a goroutine without knowing how it will stop][28]
|
||||
2. [Simplicity Debt][29]
|
||||
3. [Curious Channels][30]
|
||||
4. [Let’s talk about logging][31]
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://dave.cheney.net/2020/02/23/the-zen-of-go
|
||||
|
||||
作者:[Dave Cheney][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://dave.cheney.net/author/davecheney
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.gophercon.org.il
|
||||
[2]: https://the-zen-of-go.netlify.com
|
||||
[3]: https://www.youtube.com/watch?v=yd_rtwYaXps
|
||||
[4]: https://dave.cheney.net/wp-content/uploads/2020/02/1011226.jpg
|
||||
[5]: https://dave.cheney.net/wp-content/uploads/2020/02/mean-girls-you-cant-sit-with-us-main.jpg
|
||||
[6]: http://go-proverbs.github.io
|
||||
[7]: https://danluu.com/microsoft-culture/
|
||||
[8]: https://dave.cheney.net/wp-content/uploads/2020/02/Lucovsky.001.jpeg
|
||||
[9]: https://www.python.org/dev/peps/pep-0020/
|
||||
[10]: https://dave.cheney.net/2019/01/08/avoid-package-names-like-base-util-or-common
|
||||
[11]: https://twitter.com/davecheney/status/539576755254611968?ref_src=twsrc%5Etfw
|
||||
[12]: tmp.iUoDiQyXMU#easy-footnote-bottom-1-3936 (This part of the talk had several screenshots of the landing pages for the websites for <a href="https://www.ruby-lang.org/en/">Ruby</a>, <a href="https://swift.org">Swift</a>, <a href="https://elm-lang.org">Elm</a>, <a href="https://golang.org">Go</a>, <a href="https://nodejs.org/en/">NodeJS</a>, <a href="https://www.python.org">Python</a>, <a href="https://www.rust-lang.org">Rust</a>, highlighting how the language described itself.)
|
||||
[13]: https://changelog.com/gotime/91
|
||||
[14]: https://medium.com/@matryer/line-of-sight-in-code-186dd7cdea88
|
||||
[15]: http://sweng.the-davies.net/Home/rustys-api-design-manifesto
|
||||
[16]: https://www.youtube.com/watch?v=rFejpH_tAHM
|
||||
[17]: tmp.iUoDiQyXMU#easy-footnote-bottom-2-3936 (I tend to pick on <code>net/http</code> a lot, and this is not because it is bad, in fact it is the opposite, it is the most successful, oldest, most used API in the Go codebase. And because of that its design, evolution, and shortcoming have been thoroughly picked over. Think of this as flattery, not criticism.)
|
||||
[18]: https://godoc.org/golang.org/x/sync/errgroup
|
||||
[19]: https://www.ruby-lang.org/en/
|
||||
[20]: https://swift.org
|
||||
[21]: https://elm-lang.org
|
||||
[22]: https://golang.org
|
||||
[23]: https://nodejs.org/en/
|
||||
[24]: https://www.python.org
|
||||
[25]: https://www.rust-lang.org
|
||||
[26]: tmp.iUoDiQyXMU#easy-footnote-1-3936
|
||||
[27]: tmp.iUoDiQyXMU#easy-footnote-2-3936
|
||||
[28]: https://dave.cheney.net/2016/12/22/never-start-a-goroutine-without-knowing-how-it-will-stop (Never start a goroutine without knowing how it will stop)
|
||||
[29]: https://dave.cheney.net/2017/06/15/simplicity-debt (Simplicity Debt)
|
||||
[30]: https://dave.cheney.net/2013/04/30/curious-channels (Curious Channels)
|
||||
[31]: https://dave.cheney.net/2015/11/05/lets-talk-about-logging (Let’s talk about logging)
|
||||
@@ -1,237 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Make free encrypted backups to the cloud on Fedora)
|
||||
[#]: via: (https://fedoramagazine.org/make-free-encrypted-backups-to-the-cloud-on-fedora/)
|
||||
[#]: author: (Curt Warfield https://fedoramagazine.org/author/rcurtiswarfield/)
|
||||
|
||||
Make free encrypted backups to the cloud on Fedora
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
Most free cloud storage is limited to 5GB or less. Even Google Drive is limited to 15GB. While not heavily advertised, IBM offers free accounts with a whopping **25GB** of cloud storage for free. This is not a limited time offer, and you don’t have to provide a credit card. It’s absolutely free! Better yet, since it’s S3 compatible, most of the S3 tools available for backups should work fine.
|
||||
|
||||
This article will show you how to use restic for encrypted backups onto this free storage. Please also refer to [this previous Magazine article about installing and configuring restic.][2] Let’s get started!
|
||||
|
||||
### Creating your free IBM account and storage
|
||||
|
||||
Head over to the IBM cloud services site and follow the steps to sign up for a free account here: <https://cloud.ibm.com/registration>. You’ll need to verify your account from the email confirmation that IBM sends to you.
|
||||
|
||||
Then log in to your account to bring up your dashboard, at <https://cloud.ibm.com/>.
|
||||
|
||||
Click on the **Create resource** button.
|
||||
|
||||
![][3]
|
||||
|
||||
Click on **Storage** and then **Object Storage**.
|
||||
|
||||
![][4]
|
||||
|
||||
Next click on the **Create Bucket** button.
|
||||
|
||||
![][5]
|
||||
|
||||
This brings up the **Configure your resource** section.
|
||||
|
||||
![][6]
|
||||
|
||||
Next, click on the ****Create** button to use the default settings.
|
||||
|
||||
![][7]
|
||||
|
||||
Under **Predefined buckets** click on the **Standard** box:
|
||||
|
||||
![][8]
|
||||
|
||||
A unique bucket name is automatically created, but it’s suggested that you change this.
|
||||
|
||||
![][9]
|
||||
|
||||
In this example, the bucket name is changed to __freecloudstorage_._**
|
||||
|
||||
Click on the **Next** button after choosing a bucket name:
|
||||
|
||||
![][10]
|
||||
|
||||
Continue to click on the **Next** button until you get the the **Summary** page:
|
||||
|
||||
![][11]
|
||||
|
||||
Scroll down to the **Endpoints** section.
|
||||
|
||||
![][12]
|
||||
|
||||
The information in the **Public** section is the location of your bucket. This is what you need to specify in restic when you create your backups. In this example, the location is **s3.us-south.cloud-object-storage.appdomain.cloud**.
|
||||
|
||||
### Making your credentials
|
||||
|
||||
The last thing that you need to do is create an access ID and secret key. To start, click on **Service credentials**.
|
||||
|
||||
![][13]
|
||||
|
||||
Click on the **New credential** button.
|
||||
|
||||
![][14]
|
||||
|
||||
Choose a name for your credential, make sure you check the **Include HMAC Credential** box and then click on the **Add** button. In this example I’m using the name **resticbackup**.
|
||||
|
||||
![][15]
|
||||
|
||||
Click on **View credentials**.
|
||||
|
||||
![][16]
|
||||
|
||||
The _access_key_id_ and _secret_access_key_ is what you are looking for. (For obvious reasons, the author’s details here are obscured.)
|
||||
|
||||
You will need to export these by calling them with the _export_ alias in the shell, or putting them into a backup script.
|
||||
|
||||
![][17]
|
||||
|
||||
### Preparing a new repository
|
||||
|
||||
Restic refers to your backup as a _repository_, and can make backups to any bucket on your IBM cloud account. First, setup the following environment variables using your _access_key_id_ and _secret_access_key_ that you retrieved from your IBM cloud bucket. These can also be set in any backup script you may create.
|
||||
|
||||
```
|
||||
$ export AWS_ACCESS_KEY_ID=<MY_ACCESS_KEY>
|
||||
$ export AWS_SECRET_ACCESS_KEY=<MY_SECRET_ACCESS_KEY>
|
||||
```
|
||||
|
||||
Even though you are using IBM Cloud and not AWS, as previously mentioned, IBM Cloud storage is S3 compatible, and restic uses its interal AWS commands for any S3 compatible storage. So these AWS keys really refer to the keys from your IBM bucket.
|
||||
|
||||
Create the repository by initializing it. A prompt appears for you to type a password for the repository. _**Do not lose this password because your data is irrecoverable without it!**_
|
||||
|
||||
```
|
||||
restic -r s3:http://PUBLIC_ENDPOINT_LOCATION/BUCKET init
|
||||
```
|
||||
|
||||
The _PUBLIC_ENDPOINT_LOCATION_ was specified in the Endpoint section of your Bucket summary.
|
||||
|
||||
![][18]
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
$ restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage init
|
||||
```
|
||||
|
||||
### Creating backups
|
||||
|
||||
Now it’s time to backup some data. Backups are called _snapshots_. Run the following command and enter the repository password when prompted.
|
||||
|
||||
```
|
||||
restic -r s3:http://PUBLIC_ENDPOINT_LOCATION/BUCKET backup files_to_backup
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
$ restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage backup Documents/
|
||||
Enter password for repository:
|
||||
repository 106a2eb4 opened successfully, password is correct
|
||||
Files: 51 new, 0 changed, 0 unmodified
|
||||
Dirs: 0 new, 0 changed, 0 unmodified
|
||||
Added to the repo: 11.451 MiB
|
||||
processed 51 files, 11.451 MiB in 0:06
|
||||
snapshot 611e9577 saved
|
||||
```
|
||||
|
||||
### Restoring from backups
|
||||
|
||||
Now that you’ve backed up some files, it’s time to make sure you know how to restore them. To get a list of all of your backup snapshots, use this command:
|
||||
|
||||
```
|
||||
restic -r s3:http://PUBLIC_ENDPOINT_LOCATION/BUCKET snapshots
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
$ restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage snapshots
|
||||
Enter password for repository:
|
||||
ID Date Host Tags Directory
|
||||
-------------------------------------------------------------------
|
||||
106a2eb4 2020-01-15 15:20:42 client /home/curt/Documents
|
||||
```
|
||||
|
||||
To restore an entire snapshot, run a command like this:
|
||||
|
||||
```
|
||||
restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage restore snapshotID --target restoreDirectory
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
$ restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage restore 106a2eb4 --target ~
|
||||
Enter password for repository:
|
||||
repository 106a2eb4 opened successfully, password is correct
|
||||
restoring <Snapshot 106a2eb4 of [/home/curt/Documents]
|
||||
```
|
||||
|
||||
If the directory still exists on your system, be sure to specify a different location for the *restoreDirectory. *For example:
|
||||
|
||||
```
|
||||
restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage restore 106a2eb4 --target /tmp
|
||||
```
|
||||
|
||||
To restore an individual file, run a command like this:
|
||||
|
||||
```
|
||||
restic -r s3:http://PUBLIC_ENDPOINT_LOCATION/BUCKET restore snapshotID --target restoreDirectory --include filename
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
$ restic -r s3:http://s3.us-south.cloud-object-storage.appdomain.cloud/freecloudstorage restore 106a2eb4 --target /tmp --include file1.txt
|
||||
Enter password for repository:
|
||||
restoring <Snapshot 106a2eb4 of [/home/curt/Documents] at 2020-01-16 15:20:42.833131988 -0400 EDT by curt@client> to /tmp
|
||||
```
|
||||
|
||||
* * *
|
||||
|
||||
_Photo by [Alex Machado][19] on [Unsplash][20]._
|
||||
|
||||
[EDITORS NOTE: The Fedora Project is [sponsored][21] by [Red Hat][22], which is owned by [IBM][23].]
|
||||
|
||||
[EDITORS NOTE: Updated at 1647 UTC on 24 February 2020 to correct a broken link.]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/make-free-encrypted-backups-to-the-cloud-on-fedora/
|
||||
|
||||
作者:[Curt Warfield][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/rcurtiswarfield/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2020/01/encrypted-backups-ibm-cloud-816x345.jpg
|
||||
[2]: https://fedoramagazine.org/use-restic-encrypted-backups/
|
||||
[3]: https://fedoramagazine.org/wp-content/uploads/2020/01/ibmclouddash-3-e1579713553261.png
|
||||
[4]: https://fedoramagazine.org/wp-content/uploads/2020/01/ibmcloudresourcestorage-3.png
|
||||
[5]: https://fedoramagazine.org/wp-content/uploads/2020/01/ibmcloudbucket-3.png
|
||||
[6]: https://fedoramagazine.org/wp-content/uploads/2020/01/ibmcloudbucket2.png
|
||||
[7]: https://fedoramagazine.org/wp-content/uploads/2020/01/ibmcloudbucket3-e1579713758635.png
|
||||
[8]: https://fedoramagazine.org/wp-content/uploads/2020/01/ibmcloudbucket4.png
|
||||
[9]: https://fedoramagazine.org/wp-content/uploads/2020/01/createbucket1.png
|
||||
[10]: https://fedoramagazine.org/wp-content/uploads/2020/01/next.png
|
||||
[11]: https://fedoramagazine.org/wp-content/uploads/2020/01/bucketsummary-1024x368.png
|
||||
[12]: https://fedoramagazine.org/wp-content/uploads/2020/01/endpoints-1024x272.png
|
||||
[13]: https://fedoramagazine.org/wp-content/uploads/2020/01/servicecreds.png
|
||||
[14]: https://fedoramagazine.org/wp-content/uploads/2020/01/newcred.png
|
||||
[15]: https://fedoramagazine.org/wp-content/uploads/2020/01/addnewcred.png
|
||||
[16]: https://fedoramagazine.org/wp-content/uploads/2020/01/keys-1024x298.png
|
||||
[17]: https://fedoramagazine.org/wp-content/uploads/2020/01/keys2.png
|
||||
[18]: https://fedoramagazine.org/wp-content/uploads/2020/01/publicendpoint.png
|
||||
[19]: https://unsplash.com/@alexmachado?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
[20]: https://unsplash.com/s/photos/backups-to-cloud?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
[21]: https://getfedora.org/sponsors/
|
||||
[22]: https://redhat.com
|
||||
[23]: https://www.ibm.com/cloud/redhat
|
||||
@@ -1,411 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Fedora’s gaggle of desktops)
|
||||
[#]: via: (https://fedoramagazine.org/fedoras-gaggle-of-desktops/)
|
||||
[#]: author: (Troy Dawson https://fedoramagazine.org/author/tdawson/)
|
||||
|
||||
Fedora’s gaggle of desktops
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
There are 38 different desktops or window managers in Fedora 31. You could try a different one every day for a month, and still have some left over. Some have very few features. Some have so many features they are called a desktop environment. This article can’t go into detail on each, but it’s interesting to see the whole list in one place.
|
||||
|
||||
### Criteria for desktops
|
||||
|
||||
To be on this list, the desktop must show up on the desktop manager’s selection list. If the desktop has more than one entry in the desktop manager list, they are counted just as that one desktop. An example is “GNOME”, “GNOME Classic” and “GNOME (Wayland).” These all show up on the desktop manager list, but they are still just GNOME.
|
||||
|
||||
### List of desktops
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
#### [**9wm**][2]
|
||||
|
||||
```
|
||||
Emulation of the Plan 9 window manager 8 1/2
|
||||
dnf install 9wm
|
||||
```
|
||||
|
||||
#### [**awesome**][3]
|
||||
|
||||
```
|
||||
Highly configurable, framework window manager for X. Fast, light and extensible
|
||||
https://fedoramagazine.org/5-cool-tiling-window-managers/
|
||||
dnf install awesome
|
||||
```
|
||||
|
||||
#### [**blackbox**][4]
|
||||
|
||||
```
|
||||
Very small and fast Window Manager
|
||||
Fedora uses the maintained fork on github
|
||||
dnf install blackbox
|
||||
```
|
||||
|
||||
#### [**bspwm**][5]
|
||||
|
||||
```
|
||||
A tiling window manager based on binary space partitioning
|
||||
https://github.com/windelicato/dotfiles/wiki/bspwm-for-dummies
|
||||
dnf install bspwm
|
||||
```
|
||||
|
||||
#### **[byobu][6]**
|
||||
|
||||
```
|
||||
Light-weight, configurable window manager built upon GNU screen
|
||||
dnf install byobu
|
||||
```
|
||||
|
||||
#### **[Cinnamon][7]**
|
||||
|
||||
```
|
||||
Cinnamon provides a desktop with a traditional layout, advanced features, easy to use, powerful and flexible.
|
||||
https://projects.linuxmint.com/cinnamon/
|
||||
https://opensource.com/article/19/12/cinnamon-linux-desktop
|
||||
dnf group install "Cinnamon Desktop"
|
||||
```
|
||||
|
||||
#### **[cwm][8]**
|
||||
|
||||
```
|
||||
Calm Window Manager by OpenBSD project
|
||||
https://steemit.com/technology/@jamesdeagle/the-calm-window-manager-cwm-a-quick-start-guide
|
||||
dnf install cwm
|
||||
```
|
||||
|
||||
#### **[Deepin][9]**
|
||||
|
||||
```
|
||||
Deepin desktop is the desktop environment released with deepin (the linux distribution). It aims at being elegant and easy to use.
|
||||
dnf group install "Deepin Desktop"
|
||||
(optional) dnf group install "Deepin Desktop Office" "Media packages for Deepin Desktop"
|
||||
```
|
||||
|
||||
#### **[dwm][10]**
|
||||
|
||||
```
|
||||
Dynamic window manager for X
|
||||
https://fedoramagazine.org/lets-try-dwm-dynamic-window-manger/
|
||||
https://fedoramagazine.org/5-cool-tiling-window-managers/
|
||||
dnf install dwm
|
||||
(optional) dnf install dwm-user
|
||||
```
|
||||
|
||||
#### **[enlightenment][11]**
|
||||
|
||||
```
|
||||
Enlightenment window manager
|
||||
https://opensource.com/article/19/12/linux-enlightenment-desktop
|
||||
dnf install enlightenment
|
||||
```
|
||||
|
||||
#### **[e16][11]**
|
||||
|
||||
```
|
||||
The Enlightenment window manager, DR16
|
||||
dnf install e16
|
||||
(optional) dnf install e16-epplets e16-keyedit e16-themes
|
||||
```
|
||||
|
||||
#### **[fluxbox][12]**
|
||||
|
||||
```
|
||||
Window Manager based on Blackbox
|
||||
dnf install fluxbox
|
||||
(optional) dnf install fluxbox-pulseaudio fluxbox-vim-syntax
|
||||
```
|
||||
|
||||
#### **[fvwm][13]**
|
||||
|
||||
```
|
||||
Highly configurable multiple virtual desktop window manager
|
||||
http://www.fvwm.org/
|
||||
https://opensource.com/article/19/12/fvwm-linux-desktop
|
||||
dnf install fvwm
|
||||
```
|
||||
|
||||
#### **[GNOME][14]**
|
||||
|
||||
```
|
||||
GNOME is a highly intuitive and user friendly desktop environment.
|
||||
* both X11 and wayland
|
||||
https://opensource.com/article/19/12/gnome-linux-desktop
|
||||
https://fedoramagazine.org/3-simple-and-useful-gnome-shell-extensions/
|
||||
dnf group install "GNOME"
|
||||
(optional but large) dnf group install "Fedora Workstation"
|
||||
```
|
||||
|
||||
#### **[herbstluftwm][15]**
|
||||
|
||||
```
|
||||
A manual tiling window manager
|
||||
https://opensource.com/article/19/12/herbstluftwm-linux-desktop
|
||||
dnf install herbstluftwm
|
||||
(optional) dnf install herbstluftwm-zsh herbstluftwm-fish
|
||||
```
|
||||
|
||||
#### **[i3][16]**
|
||||
|
||||
```
|
||||
Improved tiling window manager
|
||||
https://fedoramagazine.org/getting-started-i3-window-manager/
|
||||
https://fedoramagazine.org/using-i3-with-multiple-monitors/
|
||||
dnf install i3
|
||||
(optional) dnf install i3-doc i3-ipc
|
||||
```
|
||||
|
||||
#### **[icewm][17]**
|
||||
|
||||
```
|
||||
Window manager designed for speed, usability, and consistency
|
||||
https://fedoramagazine.org/icewm-a-really-cool-desktop/
|
||||
dnf install icewm
|
||||
(optional) dnf install icewm-minimal-session
|
||||
```
|
||||
|
||||
#### **[jwm][18]**
|
||||
|
||||
```
|
||||
Joe's Window Manager
|
||||
https://opensource.com/article/19/12/joes-window-manager-linux-desktop
|
||||
dnf install jwm
|
||||
```
|
||||
|
||||
#### **[KDE Plasma Desktop][19]**
|
||||
|
||||
```
|
||||
The KDE Plasma Workspaces, a highly-configurable graphical user interface which includes a panel, desktop, system icons and desktop widgets, and many powerful KDE applications.
|
||||
* both X11 and wayland
|
||||
https://opensource.com/article/19/12/linux-kde-plasma
|
||||
https://fedoramagazine.org/installing-kde-plasma-5/
|
||||
dnf group install "KDE Plasma Workspaces"
|
||||
(optional) dnf group install "KDE Applications" "KDE Educational applications" "KDE Multimedia support" "KDE Office" "KDE Telepathy"
|
||||
(optional for wayland) dnf install kwin-wayland plasma-workspace-wayland
|
||||
```
|
||||
|
||||
#### **[lumina][20]**
|
||||
|
||||
```
|
||||
A lightweight, portable desktop environment
|
||||
https://opensource.com/article/19/12/linux-lumina-desktop
|
||||
dnf install lumina-desktop
|
||||
(optional) dnf install lumina-*
|
||||
```
|
||||
|
||||
#### **[LXDE][21]**
|
||||
|
||||
```
|
||||
LXDE is a lightweight X11 desktop environment designed for computers with low hardware specifications like netbooks, mobile devices or older computers.
|
||||
https://opensource.com/article/19/12/lxqt-lxde-linux-desktop
|
||||
dnf group install "LXDE Desktop"
|
||||
(optional) dnf group install "LXDE Office" "Multimedia support for LXDE"
|
||||
```
|
||||
|
||||
#### **[LXQt][22]**
|
||||
|
||||
```
|
||||
LXQt is a lightweight X11 desktop environment designed for computers with low hardware specifications like netbooks, mobile devices or older computers.
|
||||
https://opensource.com/article/19/12/lxqt-lxde-linux-desktop
|
||||
dnf group install "LXQt Desktop"
|
||||
(optional) dnf group install "LXQt Office" "Multimedia support for LXQt"
|
||||
```
|
||||
|
||||
#### **[MATE][23]**
|
||||
|
||||
```
|
||||
MATE Desktop is based on GNOME 2 and provides a powerful graphical user interface for users who seek a simple easy to use traditional desktop interface.
|
||||
https://opensource.com/article/19/12/mate-linux-desktop
|
||||
https://fedoramagazine.org/installing-another-desktop/
|
||||
dnf group install "MATE Desktop"
|
||||
(optional) dnf group install "MATE Applications"
|
||||
```
|
||||
|
||||
#### **[musca][24]**
|
||||
|
||||
```
|
||||
A simple dynamic window manager fox X
|
||||
dnf install musca
|
||||
```
|
||||
|
||||
#### **[openbox][25]**
|
||||
|
||||
```
|
||||
A highly configurable and standards-compliant X11 window manager
|
||||
https://opensource.com/article/19/12/openbox-linux-desktop
|
||||
https://fedoramagazine.org/openbox-fedora/
|
||||
dnf install openbox
|
||||
(optional) dnf install openbox-kde openbox-theme-mistral-thin-dark
|
||||
```
|
||||
|
||||
#### **[Pantheon][26]**
|
||||
|
||||
```
|
||||
The Pantheon desktop environment is the DE that powers elementaryOS.
|
||||
https://github.com/elementary
|
||||
https://opensource.com/article/19/12/pantheon-linux-desktop
|
||||
dnf group install "Pantheon Desktop"
|
||||
(optional) dnf install elementary-capnet-assist elementary-greeter elementary-shortcut-overlay
|
||||
```
|
||||
|
||||
#### **[pekwm][27]**
|
||||
|
||||
```
|
||||
A small and flexible window manager
|
||||
https://opensource.com/article/19/12/pekwm-linux-desktop
|
||||
dnf install pekwm
|
||||
```
|
||||
|
||||
#### **[qtile][28]**
|
||||
|
||||
```
|
||||
A pure-Python tiling window manager
|
||||
https://fedoramagazine.org/5-cool-tiling-window-managers/
|
||||
dnf install qtile
|
||||
```
|
||||
|
||||
#### **[ratpoison][29]**
|
||||
|
||||
```
|
||||
Minimalistic window manager
|
||||
https://opensource.com/article/19/12/ratpoison-linux-desktop
|
||||
dnf install ratpoison
|
||||
```
|
||||
|
||||
#### **[sawfish][30]**
|
||||
|
||||
```
|
||||
An extensible window manager for the X Window System
|
||||
dnf install sawfish
|
||||
(optional) dnf install sawfish-pager
|
||||
```
|
||||
|
||||
#### **[spectrwm][31]**
|
||||
|
||||
```
|
||||
Minimalist tiling window manager written in C
|
||||
dnf install spectrwm
|
||||
```
|
||||
|
||||
#### **[Sugar][32]**
|
||||
|
||||
```
|
||||
A software playground for learning about learning.
|
||||
* Possibly the most unique desktop of this list.
|
||||
dnf group install "Sugar Desktop Environment"
|
||||
(optional) dnf group install "Additional Sugar Activities"
|
||||
```
|
||||
|
||||
#### **[sway][33]**
|
||||
|
||||
```
|
||||
i3-compatible window manager for Wayland
|
||||
* Wayland only
|
||||
https://fedoramagazine.org/setting-up-the-sway-window-manager-on-fedora/
|
||||
https://fedoramagazine.org/5-cool-tiling-window-managers/
|
||||
dnf install sway
|
||||
```
|
||||
|
||||
#### **[twm][34]**
|
||||
|
||||
```
|
||||
X.Org X11 twm window manager
|
||||
https://en.wikipedia.org/wiki/Twm
|
||||
https://opensource.com/article/19/12/twm-linux-desktop
|
||||
dnf install xorg-x11-twm
|
||||
```
|
||||
|
||||
#### **[WindowMaker][35]**
|
||||
|
||||
```
|
||||
A fast, feature rich Window Manager
|
||||
https://opensource.com/article/19/12/linux-window-maker-desktop
|
||||
dnf install WindowMaker
|
||||
(optional) dnf install WindowMaker-extra
|
||||
```
|
||||
|
||||
#### **[wmx][36]**
|
||||
|
||||
```
|
||||
A really simple window manager for X
|
||||
dnf install wmx
|
||||
```
|
||||
|
||||
#### **[XFCE][37]**
|
||||
|
||||
```
|
||||
A lightweight desktop environment that works well on low end machines.
|
||||
https://opensource.com/article/19/12/xfce-linux-desktop
|
||||
dnf group install "Xfce Desktop"
|
||||
(optional) dnf group install "Applications for the Xfce Desktop" "Extra plugins for the Xfce panel" "Multimedia support for Xfce" "Xfce Office"
|
||||
```
|
||||
|
||||
#### **[xmonad][38]**
|
||||
|
||||
```
|
||||
A tiling window manager
|
||||
dnf install xmonad
|
||||
(optional) dnf install xmonad-mate
|
||||
```
|
||||
|
||||
* * *
|
||||
|
||||
_Photo by [Annie Spratt][39] on [Unsplash][40]._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/fedoras-gaggle-of-desktops/
|
||||
|
||||
作者:[Troy Dawson][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/tdawson/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2020/01/gaggle-desktops-816x345.jpg
|
||||
[2]: https://github.com/9wm/9wm
|
||||
[3]: https://awesomewm.org/
|
||||
[4]: https://github.com/bbidulock/blackboxwm
|
||||
[5]: https://github.com/baskerville/bspwm
|
||||
[6]: https://byobu.org/
|
||||
[7]: https://github.com/linuxmint/cinnamon
|
||||
[8]: https://github.com/leahneukirchen/cwm
|
||||
[9]: https://www.deepin.org/en/dde/
|
||||
[10]: http://dwm.suckless.org/
|
||||
[11]: https://www.enlightenment.org/
|
||||
[12]: http://fluxbox.org/
|
||||
[13]: https://github.com/fvwmorg/fvwm
|
||||
[14]: https://www.gnome.org/
|
||||
[15]: http://herbstluftwm.org/
|
||||
[16]: https://i3wm.org/
|
||||
[17]: https://ice-wm.org/
|
||||
[18]: http://joewing.net/projects/jwm/
|
||||
[19]: https://kde.org/
|
||||
[20]: https://lumina-desktop.org/
|
||||
[21]: https://lxde.org/
|
||||
[22]: https://lxqt.org/
|
||||
[23]: https://mate-desktop.org/
|
||||
[24]: https://github.com/enticeing/musca
|
||||
[25]: http://openbox.org
|
||||
[26]: https://elementary.io/
|
||||
[27]: http://www.pekwm.org/
|
||||
[28]: http://qtile.org
|
||||
[29]: http://www.nongnu.org/ratpoison/
|
||||
[30]: http://sawfish.wikia.com/
|
||||
[31]: https://github.com/conformal/spectrwm
|
||||
[32]: https://sugarlabs.org/
|
||||
[33]: https://github.com/swaywm/sway
|
||||
[34]: https://www.x.org/releases/X11R7.6/doc/man/man1/twm.1.xhtml
|
||||
[35]: http://www.windowmaker.org
|
||||
[36]: http://www.all-day-breakfast.com/wmx/
|
||||
[37]: https://www.xfce.org/
|
||||
[38]: https://hackage.haskell.org/package/xmonad
|
||||
[39]: https://unsplash.com/@anniespratt?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
[40]: https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
@@ -1,168 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Using LibreOffice for your open source budgeting tool)
|
||||
[#]: via: (https://opensource.com/article/20/3/libreoffice-open-source-budget)
|
||||
[#]: author: (Jess Weichler https://opensource.com/users/cyanide-cupcake)
|
||||
|
||||
Using LibreOffice for your open source budgeting tool
|
||||
======
|
||||
Figure out where your money is going with this LibreOffice Calc budget
|
||||
template.
|
||||
![scientific calculator][1]
|
||||
|
||||
Budgets can be intimidating for beginners. It can feel overwhelming to think about money, much less about how to keep track of it. But it's important to know where your money is coming and going.
|
||||
|
||||
In this article, I'll step through a sample budget by explaining the logic behind important money decisions as well as the formulas you need to automate the process. Fortunately, LibreOffice makes it easy for anyone to keep their yearly budget in check, even the math-averse.
|
||||
|
||||
### Getting started
|
||||
|
||||
Begin by downloading and installing [LibreOffice][2], if you don't already have it. Next, download my [LibreOffice Calc template][3], which you can use as a starting point to create your own budget to meet your spending and savings goals.
|
||||
|
||||
It's important to interact with your spreadsheet frequently. You can input transactions as they happen, daily, or weekly. You even could save up your receipts to calculate all your expenses at the end of the month, but this can be a hard slog. You want budgeting to be as quick and easy as possible.
|
||||
|
||||
### Categories
|
||||
|
||||
The first step to creating a budget is to decide on the categories you want to track. These categories can be as simple or as complex as you like. Think about what is useful to your personal situation and financial goals. You can easily add or change categories as your needs change.
|
||||
|
||||
The template has a number of example categories you can start with. There is no right or wrong way to choose categories; your budget has to work for you. Look through the list in Column A of the **Budget** tab and decide which ones to keep, which to delete, and any others you want to add. Then edit that list to align with your personal income and expense situation.
|
||||
|
||||
#### Create category drop-down menus
|
||||
|
||||
![Budget categories][4]
|
||||
|
||||
The template uses a drop-down menu to make it easy to assign categories to income and expenses. You can view them on the **Monthly** sheets (accessed with the tabs at the bottom of the LibreOffice window). Click on a cell in Column C, and a drop-down arrow will appear on the right. Click the arrow, and you'll see the example categories. You may need to change some of them so that they will match the categories in your budget (Column A of the **Budget** tab).
|
||||
|
||||
To add or remove categories from the dropdown menu, click on Column C in a Monthly sheet to select all **Category** cells. Then, in the main menu, select **Data** > **Validity**. This opens up a dialog box.
|
||||
|
||||
In **Validity**, select the **Criteria** tab, then click on the arrow to the right of **Allow**, set it to **List**, and type the categories you want to use in the **Entries** box. Type one category per line. Make sure to use the exact same categories you used on the **Budget** sheet in Column A.
|
||||
|
||||
### Estimating your budget
|
||||
|
||||
Once you have defined your categories, it's time to estimate how much you expect to earn and spend. You can calculate these amounts monthly, yearly, or using a mix of the two.
|
||||
|
||||
Your first year of budgeting estimates won't be perfect, and you may be surprised at how much or little you spend on certain categories. But doing this will help you get a realistic idea of where your money is going. You can make adjustments in your second year to create a more accurate budget based upon what you spend in year one.
|
||||
|
||||
In Column B (**Monthly Estimate**) of the **Budget** tab, enter your anticipated _monthly_ income and expenses for each category (an exception is the **Charity** row, which is automatically calculated as a percentage of your income). For _annual_ expenses and income (e.g., taxes, insurance, tuition, etc.), enter them in Column P (**Yearly Estimate**).
|
||||
|
||||
#### Calculating the annual cost of monthly expenses
|
||||
|
||||
Most expenses occur monthly. To find the yearly cost of a monthly expense, multiply your monthly estimate by 12. You could do this manually for each category, but it is much easier to use formulas.
|
||||
|
||||
Formulas are automated calculations that determine the value of a cell. Formulas do all the heavy lifting, so you don't have to do a lot of sums in your head.
|
||||
|
||||
In the template, you can use the **Yearly Estimate** column to make an equation to annualize a monthly expense or income. In a cell in Column P, type this:
|
||||
|
||||
|
||||
```
|
||||
`=SUM(x*12)`
|
||||
```
|
||||
|
||||
but change _x_ to the name of the **Monthly Estimate** cell you want to use. (For example, to calculate the annual cost of your phone service using the template, the formula would read **=SUM(B12*13)**.)
|
||||
|
||||
#### Calculating the monthly cost of yearly expenses
|
||||
|
||||
You may pay some expenses, such as car insurance, only once a year. You can either ignore these expenses in the monthly estimates or put money aside for them in your budget each month.
|
||||
|
||||
If you want to do the latter, you need to divide your **Yearly Estimate** by 12 and put that amount into your monthly budget. To do so, place this equation in the appropriate cell in the **Monthly Estimate** column:
|
||||
|
||||
|
||||
```
|
||||
`=SUM(x/12)`
|
||||
```
|
||||
|
||||
where _x_ is the corresponding **Yearly Estimate** cell on your spreadsheet (from Column P).
|
||||
|
||||
#### Finding percentages
|
||||
|
||||
If you want to donate or save a percentage of your income, there's a function for that, too!
|
||||
|
||||
The common recommendation is to put aside 20% of your take-home pay for savings. While I don't focus on this too much, I do find it helpful to see if I'm meeting my savings goals from month to month.
|
||||
|
||||
For instance, Row 32 of the **Budget** tab template uses this formula to calculate the 20% of your income that you should allocate to savings:
|
||||
|
||||
|
||||
```
|
||||
`=SUM(B2*0.2)`
|
||||
```
|
||||
|
||||
This same method can be used if you give a percentage (e.g., 10%) of your income to charity (Row 21 of the template):
|
||||
|
||||
|
||||
```
|
||||
`=SUM(B2*-0.1)`
|
||||
```
|
||||
|
||||
This formula uses a negative percentage because donating to charity is an expense.
|
||||
|
||||
### Entering monthly income and expenses
|
||||
|
||||
The template pulls data totals from the **Monthly** sheets (the tabs at the bottom of the spreadsheet) to populate Columns C through N on the **Budget** sheet.
|
||||
|
||||
It's useful to place each month's transactions on separate sheets of your budget spreadsheet. By keeping your receipts from purchases and entering them into each month's sheet, you create a digital record of your money.
|
||||
|
||||
Enter income as positive numbers and expenses as negative numbers. Select the appropriate category using the drop-down in the **Category** column.
|
||||
|
||||
In LibreOffice, the **SUMIF** function can look at values in a specific column and extract only the ones that occur next to a specific word. My template uses a **SUMIF** formula to extract values based on the adjacent category in order to enter an amount in the correct cell on the **Budget** tab. For example, to enter January's internet expenses into the **Budget** spreadsheet, enter this formula in cell C12:
|
||||
|
||||
|
||||
```
|
||||
`=SUMIF(january.$C:$C,A12,january.$D:$D)`
|
||||
```
|
||||
|
||||
This looks at January's Column C and, if it sees an entry that contains the word in A12 on the **Budget** tab (Internet), then it extracts the number from Column D on the **January** tab and enters that value into the cell that contains the formula on the **Budget** tab (C12).
|
||||
|
||||
### Analyzing your budget data
|
||||
|
||||
#### Adding a range of numbers to calculate YTD spending
|
||||
|
||||
To see how much you have spent overall this year to date (YTD), select the cell where you want to display that data (in the template, it's cell O29, under the **YTD** column), and enter the following formula to total the range of numbers corresponding to your monthly **Total Expense**:
|
||||
|
||||
|
||||
```
|
||||
`=SUM(x:y)`
|
||||
```
|
||||
|
||||
Instead of _x_ and _y_, enter the first cell and the last cell in the range. You can type them in manually, but it's easier and less error-prone to just click and drag from the first to last cell. LibreOffice does the calculation and enters the appropriate values.
|
||||
|
||||
#### Seeing how you're doing on your budget
|
||||
|
||||
A big part of budgeting is comparing your estimates to your actual income and expenses. In the template, this is the **Budget** tab's Column Q. This column subtracts the contents of each cell in Column O (**YTD**) from Column P (**Yearly Estimate**).
|
||||
|
||||
|
||||
```
|
||||
`=SUM(x-y)`
|
||||
```
|
||||
|
||||
where _x_ and _y_ equal the corresponding cells from Column P and O. For example, using the template to calculate how much you've spent on Utilities compared to your budget, you would enter **=SUM(P11-O11)**.
|
||||
|
||||
![Budget overview][5]
|
||||
|
||||
### Tracking expenses
|
||||
|
||||
Now that your yearly budget is set up, you are ready to start meeting your financial goals.
|
||||
|
||||
It's important to look at your budget often—and it's equally important to do so without guilt. Think of this process as gathering data so that you can adjust your estimates for the next year. The primary goal of budgeting is to understand your own spending habits and refine either your expectations or your behavior so that you can plan better for how your income is used.
|
||||
|
||||
Which open source tools and apps do you use to budget? Tell us in the comments!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/3/libreoffice-open-source-budget
|
||||
|
||||
作者:[Jess Weichler][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/cyanide-cupcake
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/calculator_money_currency_financial_tool.jpg?itok=2QMa1y8c (scientific calculator)
|
||||
[2]: https://www.libreoffice.org/download/download/
|
||||
[3]: https://opensource.com/sites/default/files/uploads/budget_template_0.ods
|
||||
[4]: https://opensource.com/sites/default/files/uploads/imagebudget_cat.png (Budget categories)
|
||||
[5]: https://opensource.com/sites/default/files/uploads/imagebudget_overview.png (Budget overview)
|
||||
@@ -1,164 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Getting started with lightweight alternatives to GNU Emacs)
|
||||
[#]: via: (https://opensource.com/article/20/3/lightweight-emacs)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
Getting started with lightweight alternatives to GNU Emacs
|
||||
======
|
||||
Slimmed-down (in size and features) alternatives allow you to take your
|
||||
text editor anywhere you go.
|
||||
![Text editor on a browser, in blue][1]
|
||||
|
||||
I work on a lot of servers, and sometimes I find a host that hasn't installed [GNU Emacs][2]. There's usually a [GNU Nano][3] installation to keep me from resorting to [Vi][4], but I'm not used to Nano the way I am Emacs, and I inevitably run into complications when I try to save my document (**C-x** in Nano stands for Exit, and **C-s** locks Konsole).
|
||||
|
||||
While it would be nice to have GNU Emacs available everywhere, it's a lot of program for making a simple update to a config file. My need for a small and lightweight emacs is what took me down the path of discovering MicroEmacs, Jove, and Zile—tiny, self-contained [emacsen][5] that you can put on a thumb drive, an SD card, and nearly any server, so you'll never be without an emacs editor.
|
||||
|
||||
### Editing macros
|
||||
|
||||
The term "emacs" is a somewhat generic term in the way that only open source produces, and a portmanteau. Before there was [GNU Emacs][6], there were collections of batch process scripts (called _macros_) that could perform common tasks for a user. For instance, if you often found yourself typing "teh" instead of "the," you could either go in and correct each one manually (no small feat when your editor can't even load the entire document into memory, as was often the case in the early 1980s), or you could invoke a macro to perform a quick swap of the "e" and "h."
|
||||
|
||||
Eventually, these macros were bundled together into a package called editing macros, or EMACS for short. GNU Emacs is the most famous emacsen (yes, the -en suffix is used to describe many emacs, as in the word "oxen"), but it's not the only one. And it's certainly not the smallest. Quite the contrary, GNU Emacs is probably one of the largest.
|
||||
|
||||
Fortunately, GNU Emacs is so popular that other emacs implementations tend to mimic most of the GNU version's basic controls. If you're looking for a basic, fast, and efficient editor that isn't Vim, you'll likely be happy with any of these options.
|
||||
|
||||
### MicroEmacs
|
||||
|
||||
![µemacs][7]
|
||||
|
||||
[MicroEmacs][8], also known as uemacs (as in the Greek letter µ, which denotes "micro" in scientific notation), was written by Dave Conroy, but there's a long list of users who have cloned it and modified it. One user who maintains a personal version of µemacs is a programmer named Linus Torvalds, and his copy is available from his website, [kernel.org][9] (which also, incidentally, includes a small side project of his called [Linux][10]).
|
||||
|
||||
#### Size
|
||||
|
||||
It takes me five seconds to compile µemacs at the slowest setting I can impose on my computer, and the resulting binary is a mere 493KB. Admittedly, that's not literally "micro" compared to the typical size of a GNU Emacs download (1 millionth of 70MB is 70 bytes, by my calculation), but it's respectably small. For instance, it's easy enough to send it to yourself by email or over Signal, and certainly small enough to keep handy on every thumb drive or SD card you own.
|
||||
|
||||
By default, Linus's version expects libcurses, but you can override this setting in the Makefile so that it uses libtermcap instead. The resulting binary is independent enough to run on most Linux boxes:
|
||||
|
||||
|
||||
```
|
||||
$ ldd em
|
||||
linux-vdso.so.1
|
||||
libtermcap.so.2 => /lib64/libtermcap.so.2
|
||||
libc.so.6 => /lib64/libc.so.6
|
||||
/lib64/ld-linux-x86-64.so.2
|
||||
```
|
||||
|
||||
#### Features
|
||||
|
||||
The [keyboard shortcuts][11] are just as you'd expect. You can open files and edit them without ever realizing you're not in GNU Emacs.
|
||||
|
||||
Some advanced features are missing. For instance, there's no vertical buffer split, although there is a horizontal split. There's no eval command, so you won't use µemacs for Lisp programming.
|
||||
|
||||
The search function is also a little different from what you may be used to: instead of **C-s**, it's **M-s**, which could make all the difference if your terminal emulator accepts **Ctrl+S** as a freeze command. The help page for µemacs is very complete, so use **M-x help** to get familiar with what it has available.
|
||||
|
||||
#### License
|
||||
|
||||
The license for µemacs is custom to the project with a non-commercial condition. You're free to share, use, and modify µemacs, but you can't do anything commercial with it.
|
||||
While not as liberal a policy as I typically prefer, it's a good-enough license for personal use; just don't build a business around it.
|
||||
|
||||
### GNU Zile
|
||||
|
||||
![GNU Zile][12]
|
||||
|
||||
[GNU Zile][13] claims to be a development kit for text editors. It's meant as a framework to enable people to quickly develop their own custom text editor without having to reinvent common data structures. It's a great idea and probably very useful, but as I have no interest in making my own editor, I just use the example implementation that ships with its codebase as a pleasant, lightweight emacs.
|
||||
|
||||
The build process for the example editor (supposedly called Zemacs, although the binary it renders is named zile) is the standard [Autotools][14] procedure:
|
||||
|
||||
|
||||
```
|
||||
$ ./configure
|
||||
$ make
|
||||
```
|
||||
|
||||
#### Size
|
||||
|
||||
Compiling it from source takes me a minute on one core or about 50 seconds on six cores (the configuration process is the long part). The binary produced in the end is 1.2MB, making this the heaviest of the lightweight emacsen I use, but compared to even GNU Emacs without X (which is 14MB on my system), it's relatively trivial.
|
||||
|
||||
Of the lightweight emacsen I use, it's also the most complex. You can exclude some library links by disabling features during configuration, but here are the defaults:
|
||||
|
||||
|
||||
```
|
||||
$ ldd src/zile
|
||||
linux-vdso.so.1
|
||||
libacl.so.1 => /lib64/libacl.so.1
|
||||
libncurses.so.5 => /lib64/libncurses.so.5
|
||||
libgc.so.1 => /usr/lib64/libgc.so.1
|
||||
libc.so.6 => /lib64/libc.so.6
|
||||
libattr.so.1 => /lib64/libattr.so.1
|
||||
libdl.so.2 => /lib64/libdl.so.2
|
||||
libpthread.so.0 => /lib64/libpthread.so.0
|
||||
/lib64/ld-linux-x86-64.so.2
|
||||
```
|
||||
|
||||
#### Features
|
||||
|
||||
Zile acts a little more like GNU Emacs than µemacs or Jove, but it's still a minimal experience. But some little touches are refreshing: Tab completion happens in a buffer, you can run shell commands from the mini-buffer, and you have a good assortment of functions available. It's by no means a GNU Emacs replacement, though, and if you wander too far in search of advanced features, you'll find out why it's only 1.2MB.
|
||||
|
||||
I've been unable to find in-application help files, and the man page bundled with it is minimal. However, if you're comfortable with Emacs, Zile is a good compromise between the full 14MB (or greater, if you're using a GUI) version and the extremely lightweight implementations.
|
||||
|
||||
### Jove
|
||||
|
||||
![Jove][15]
|
||||
|
||||
[Jove][16] was my first tiny emacs and remains the smallest I've found yet. This was an easy discovery for me, as it ships with [Slackware][17] Linux and, with a surreptitious symlink, quickly became my personal replacement for the Vi binary. Jove is based on GNU Emacs, but the man page cautions that feature parity is by no means to be expected. I find Jove surprisingly feature-rich for such a small binary (in fact, this article was written in Jove version 4.17.06-9), but there's no question that renaming .emacs to .joverc does _not_ behave as you might hope.
|
||||
|
||||
#### Size
|
||||
|
||||
It takes me five seconds to compile Jove at the slowest setting (-j1) and about a second using all cores. The resulting binary, confusingly called jjove by default, is just 293KB.
|
||||
|
||||
The Jove binary is independent enough to run on most Linux boxes:
|
||||
|
||||
|
||||
```
|
||||
$ ldd jjove
|
||||
linux-vdso.so.1
|
||||
libtermcap.so.2 => /lib64/libtermcap.so.2
|
||||
libc.so.6 => /lib64/libc.so.6
|
||||
/lib64/ld-linux-x86-64.so.2
|
||||
```
|
||||
|
||||
#### Features
|
||||
|
||||
Jove has good documentation in the form of a man page. You can also get a helpful listing of all available commands by typing **M-x ?** and using the Spacebar to scroll. If you're entirely new to emacs, you can run **teachjove** to learn Jove (and emacs, accordingly).
|
||||
|
||||
Most common editing commands and keybindings work as expected. Some oddities exist; for example, there's no vertical split, and Tab completion for paths in the mini-buffer is non-existent. However, it's the smallest emacs I've found and yet has a full GNU Emacs feel to it.
|
||||
|
||||
### Try Emacs
|
||||
|
||||
If you've only ever tried GNU Emacs, then you might find that the world of emacsen is richer than you may have expected. There's a rich tradition behind emacs, and trying some of its variants, spin-offs, and alternate implementations is part of the joy of being comfortable with how emacsen work. Get to know emacs; carry a few builds around everywhere you go, and you'll never have to use a substandard editor again!
|
||||
|
||||
GNU Emacs can be much more than just a text editor. Learn how to get started.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/3/lightweight-emacs
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/browser_blue_text_editor_web.png?itok=lcf-m6N7 (Text editor on a browser, in blue)
|
||||
[2]: https://www.gnu.org/software/emacs/
|
||||
[3]: https://www.nano-editor.org/
|
||||
[4]: https://opensource.com/article/19/3/getting-started-vim
|
||||
[5]: https://www.emacswiki.org/emacs/Emacsen
|
||||
[6]: https://opensource.com/article/20/2/who-cares-about-emacs
|
||||
[7]: https://opensource.com/sites/default/files/uploads/lightweight-emacs-uemacs.jpg (µemacs)
|
||||
[8]: https://en.wikipedia.org/wiki/MicroEMACS
|
||||
[9]: https://git.kernel.org/pub/scm/editors/uemacs/uemacs.git
|
||||
[10]: https://opensource.com/tags/linux
|
||||
[11]: https://opensource.com/downloads/emacs-cheat-sheet
|
||||
[12]: https://opensource.com/sites/default/files/uploads/lightweight-emacs-zile.jpg (GNU Zile)
|
||||
[13]: https://www.gnu.org/software/zile/
|
||||
[14]: https://opensource.com/article/19/7/introduction-gnu-autotools
|
||||
[15]: https://opensource.com/sites/default/files/uploads/lightweight-emacs-jove.jpg (Jove)
|
||||
[16]: https://opensource.com/article/17/1/jove-lightweight-alternative-vim
|
||||
[17]: http://slackware.com
|
||||
@@ -1,428 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How service virtualization relates to test-driven development)
|
||||
[#]: via: (https://opensource.com/article/20/3/service-virtualization-test-driven-development)
|
||||
[#]: author: (Alex Bunardzic https://opensource.com/users/alex-bunardzic)
|
||||
|
||||
How service virtualization relates to test-driven development
|
||||
======
|
||||
Mountebank simulates services you're dependent on so autonomous teams
|
||||
can continue development activities without having to wait on anyone.
|
||||
![Person using a laptop][1]
|
||||
|
||||
The agile approach to software development relies on service virtualization to give each IT team autonomy. This approach removes blockages and allows autonomous teams to continue development activities without having to wait on anyone. That way, integration testing can commence as soon as teams start iterating/sprinting.
|
||||
|
||||
### How automated services work
|
||||
|
||||
Any automated service is available to consumers via a published endpoint. This means services can be automated only if they're made available online.
|
||||
|
||||
Any consumer wishing to leverage available automated services must be capable of sending requests to that service's endpoint via an HTTP protocol. Some of those services will, upon receiving the request via the HTTP protocol, respond by simply sending back some data. Other services may respond to receiving a request via HTTP protocol by actually performing some work. For example, a service may create a resource (for example, create an order), update a resource (update an order), or delete a resource (cancel an order).
|
||||
|
||||
All those activities get triggered via the HTTP protocol. In the simplest of cases, the action instigated by the service consumer is GET (e.g., HTTP GET). That request may arrive with some query values; those values will get used by the service to narrow down the search (such as "search for order number 12345 and return the data").
|
||||
|
||||
In more elaborate cases, a request may arrive with the instruction to POST some values; a service will accept that request and expect some values to be associated with it. Those values are usually called the payload. When the service accepts an HTTP POST request containing the payload, it will attempt to process it. It may or may not succeed in processing it, but either way, it will respond to the service consumer with a status code and an optional status message. That way, service consumers will be notified of the success/failure of their request so that they can decide what the next step should be.
|
||||
|
||||
### What is service virtualization?
|
||||
|
||||
Now that we understand how automated services work, it should be easier to understand how to virtualize them. In a nutshell, it is possible to simulate any service that is published on a hosting site. Instead of sending HTTP requests directly to the service provider's endpoint, you can interject a fake, pretend service that simulates the behavior of the real service.
|
||||
|
||||
From the service consumer's standpoint, it makes absolutely no difference whether it is interacting with a real or a fake service. The interaction remains identical.
|
||||
|
||||
### Virtualize one service
|
||||
|
||||
OK, enough talking, I'll roll up my sleeves and show how to do it in practical terms. Suppose your team is starting a new project and receives requirements in the form of a fully fleshed user story:
|
||||
|
||||
#### Authenticate user
|
||||
|
||||
_As a new app_
|
||||
_I want to authenticate the user_
|
||||
_Because we want to ensure proper security for the app_
|
||||
|
||||
#### Acceptance criteria
|
||||
|
||||
**Scenario #1:** _New app successfully authenticates the user_
|
||||
Given that the user has navigated to the login page
|
||||
And the user has submitted credentials
|
||||
When new app receives login request
|
||||
Then new app successfully authenticates the user
|
||||
And new app displays response message "User successfully logged in."
|
||||
|
||||
**Scenario #2:** _New app cannot authenticate the user on the first attempt_
|
||||
Given that the user has navigated to the login page
|
||||
And the user has submitted credentials
|
||||
When new app receives login request
|
||||
Then new app fails to successfully authenticate the user
|
||||
And new app displays response message "Incorrect login. You have 2 more attempts left."
|
||||
|
||||
**Scenario #3:** _New app cannot authenticate the user on the second attempt_
|
||||
Given that the user has navigated to the login page
|
||||
And the user has submitted credentials
|
||||
When new app receives login request
|
||||
Then new app fails to successfully authenticate the user
|
||||
And new app displays response message "Incorrect login. You have 1 more attempt left."
|
||||
|
||||
**Scenario #4:** _New app cannot authenticate the user on the third attempt_
|
||||
Given that the user has navigated to the login page
|
||||
And the user has submitted credentials
|
||||
When new app receives login request
|
||||
Then new app fails to successfully authenticate the user
|
||||
And new app displays response message "Incorrect login. You have no more attempts left."
|
||||
|
||||
The first thing to do when starting the work on this user story is to create the so-called "walking skeleton" (for this exercise, I will be using the standard **.Net Core** platform plus **xUnit.net** I discussed in my previous articles ([starting with this one][2] with [another example here][3]). Please refer to them for technical details on how to install, configure, and run the required tools.
|
||||
|
||||
Create the walking skeleton infrastructure by opening the command line and typing:
|
||||
|
||||
|
||||
```
|
||||
`mkdir AuthenticateUser`
|
||||
```
|
||||
|
||||
Then move inside the **AuthenticateUser** folder:
|
||||
|
||||
|
||||
```
|
||||
`cd AuthenticateUser`
|
||||
```
|
||||
|
||||
And create a separate folder for tests:
|
||||
|
||||
|
||||
```
|
||||
`mkdir tests`
|
||||
```
|
||||
|
||||
Move into the **tests** folder (**cd tests**) and initiate the **xUnit** framework:
|
||||
|
||||
|
||||
```
|
||||
`dotnet new xunit`
|
||||
```
|
||||
|
||||
Now move one folder up (back to **AuthenticateUser**) and create the app folder:
|
||||
|
||||
|
||||
```
|
||||
mkdir app
|
||||
cd app
|
||||
```
|
||||
|
||||
Create the scaffold necessary for C# code:
|
||||
|
||||
|
||||
```
|
||||
`dotnet new classlib`
|
||||
```
|
||||
|
||||
The walking skeleton is now ready! Open the editor of your choice and start coding.
|
||||
|
||||
### Write a failing test first
|
||||
|
||||
In the spirit of TDD, start by writing the failing test (refer to the [previous article][4] to learn why is it important to see your test fail before attempting to make it pass):
|
||||
|
||||
|
||||
```
|
||||
using System;
|
||||
using Xunit;
|
||||
using app;
|
||||
|
||||
namespace tests {
|
||||
public class UnitTest1 {
|
||||
Authenticate auth = [new][5] Authenticate();
|
||||
|
||||
[Fact]
|
||||
public void SuccessLogin(){
|
||||
var given = "credentials";
|
||||
var expected = "Successful login.";
|
||||
var actual = auth.Login(given);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This test states that if someone supplies some credentials (i.e., a secret username and password) to the **Login** method of the **Authenticate** component when it processes the request, it is expected to return the message "Successful login."
|
||||
|
||||
Of course, this is functionality that does not exist yet—the instantiated **Authenticate** module in the **SuccessLogin()** module hasn't been written yet. So you might as well go ahead and take the first stab at writing the desired functionality. Create a new file (**Authenticate.cs**) in the **app** folder and add the following code:
|
||||
|
||||
|
||||
```
|
||||
using System;
|
||||
|
||||
namespace app {
|
||||
public class Authenticate {
|
||||
public string Login(string credentials) {
|
||||
return "Not implemented";
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, navigate to the **tests** folder and run:
|
||||
|
||||
|
||||
```
|
||||
`dotnet test`
|
||||
```
|
||||
|
||||
![Output of dotnet.test][6]
|
||||
|
||||
The test fails because it was expecting a "Successful login" output but instead got the "Not implemented" output.
|
||||
|
||||
### Increasing complexity for day two operations
|
||||
|
||||
Now that you have created the "happy path" expectation and made it fail, it is time to work on implementing the functionality that will make the failing test pass. The following day, you attend the standup and report that you have started on the "Authenticate user" story. You let the team know that you have created the first failing test for the "happy path," and today, the plan is to implement the code to make the failing test pass.
|
||||
|
||||
You explain your intention to first create a **User** table containing the **username**, **password**, and other pertinent attributes. But the scrum master interrupts and explains that the **User** module is being handled by another team. It would be bad practice to duplicate the maintenance of users, as the information will quickly get out of sync. So instead of building the **User** module (which would include the authentication logic), you are to leverage the authentication services that the **User** team is working on.
|
||||
|
||||
That's great news because it saves you the trouble of having to write a lot of code to implement the **User** processing. Emboldened, you enthusiastically announce that you will quickly cobble up a function that will take user credentials and send them to the service that the **User** team has built.
|
||||
|
||||
Alas, your intentions get squashed again as you learn that the **User** team hasn't started building the **User authentication** service yet. They're still in the process of assigning user stories to the backlog. Disheartened, you resign to the fact that it will be at least a few days (if not weeks?) before you can start working on the **User authentication** story.
|
||||
|
||||
The scrum master then says that there is no reason to wait for the **User authentication** service to be built and deployed to testing. You could start developing the authentication functionality right away. But how can you do that?
|
||||
|
||||
The scrum master offers a simple suggestion: leverage service virtualization. Since all specifications for the **User** module have been solidified and signed off, you have a solid, non-volatile contract to build your solution against. The contract published by the **User** services team states that in order to authenticate a user, specific expectations must be fulfilled:
|
||||
|
||||
1. A client wishing to authenticate a user should send an **HTTP POST** request to the endpoint <http://some-domain.com/api/v1/users/login>.
|
||||
2. The **HTTP POST** sent to the above endpoint must have a **JSON** payload that contains the user credentials (i.e., username and password).
|
||||
3. Upon receiving the request, the service will attempt to log the user in. If the username and password match the information on record, the service will return an **HTTP** response containing status code 200 with the body of the response containing the message "User successfully logged in."
|
||||
|
||||
|
||||
|
||||
So, now that you know the contract details, you can start building the solution. Here's the code that connects to the endpoint, sends the **HTTP POST** request, and receives the **HTTP** response:
|
||||
|
||||
|
||||
```
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace app {
|
||||
public class Authenticate {
|
||||
HttpClient client = [new][5] HttpClient();
|
||||
string endPoint = "<http://some-domain.com/api/v1/users/login>";
|
||||
|
||||
public string Login(string credentials) {
|
||||
Task<string> response = CheckLogin(credentials);
|
||||
return response.Result;
|
||||
}
|
||||
|
||||
private async Task<string> CheckLogin(string credentials) {
|
||||
var values = [new][5] Dictionary<string, string>{{"credentials", credentials}};
|
||||
var content = [new][5] FormUrlEncodedContent(values);
|
||||
var response = await client.PostAsync(endPoint, content);
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This code won't work because <http://some-domain.com> does not exist (yet). Are you stuck now, waiting for the other team to eventually build and deploy that service?
|
||||
|
||||
Not really. Service virtualization to rescue! Let's pretend that the service is already there and continue the development.
|
||||
|
||||
### How to virtualize a service
|
||||
|
||||
One way to virtualize the **User authentication** service would be to write a new app (the new API) and run it locally. This API will mirror the contract specified by the real **User authentication** API and will only return hard-coded stubbed data (it will be a fake service).
|
||||
|
||||
Sounds like a good plan. Again, the team pushes back during the standup, questioning the need for writing, building, testing, and deploying a brand new app just to accomplish this fake functionality. It kind of wouldn't be worth the trouble because, by the time you deliver that new fake app, the other team would probably be ready with the real service.
|
||||
|
||||
So you've reached an impasse. It looks like you are forced to wait on your dependency to materialize. You've failed to control your dependencies; you now have no recourse but to work in a sequential fashion.
|
||||
|
||||
Not so fast! There is a great new tool called [mountebank][7] that is ideal for virtualizing any service. Using this tool, you can quickly stand up a local server that listens on a port you specify and takes orders. To make it simulate a service, you only have to tell it which port to listen to and which protocol to handle. The choice of protocols is:
|
||||
|
||||
* HTTP
|
||||
* HTTPS
|
||||
* SMTP
|
||||
* TCP
|
||||
|
||||
|
||||
|
||||
In this case, you need the HTTP protocol. First, install mountebank—if you have **npm** on your computer, you can simply type on the command line:
|
||||
|
||||
|
||||
```
|
||||
`npm install -g mountebank`
|
||||
```
|
||||
|
||||
After it's installed, run mountebank by typing:
|
||||
|
||||
|
||||
```
|
||||
`mb`
|
||||
```
|
||||
|
||||
At startup, mountebank will show:
|
||||
|
||||
![mountebank startup][8]
|
||||
|
||||
Now you're ready to virtualize an HTTP service. In this case, the **User authentication** service expects to receive an HTTP POST request; here is how the implemented code sends an HTTP POST request:
|
||||
|
||||
|
||||
```
|
||||
`var response = await client.PostAsync(endPoint, content);`
|
||||
```
|
||||
|
||||
You now have to establish that **endPoint**. Ideally, all virtualized services should be propped in the **localhost** server to ensure quick execution of integration tests.
|
||||
|
||||
To do that, you need to configure the **imposter**. In its bare-bones form, the **imposter** is a simple JSON collection of key-value pairs containing the definition of a port and a protocol:
|
||||
|
||||
|
||||
```
|
||||
{
|
||||
"port": 3001,
|
||||
"protocol": "http"
|
||||
}
|
||||
```
|
||||
|
||||
This imposter is configured to handle the HTTP protocol and to listen to incoming requests on port 3001.
|
||||
|
||||
Just listening to incoming HTTP requests on port 3001 is not going to do much. Once the request arrives at that port, mountebank needs to be told what to do with that request. In other words, you are virtualizing not only the availability of a service on a specific port but also the way that virtualized service is going to respond to the request.
|
||||
|
||||
To accomplish that level of service virtualization, you need to tell mountebank how to configure stubs. Each stub consists of two components:
|
||||
|
||||
1. A collection of predicates
|
||||
2. A collection of expected responses
|
||||
|
||||
|
||||
|
||||
A predicate (sometimes called a matcher) narrows down the scope of the incoming request. For example, using the HTTP protocol, you can expect more than one type of method (e.g., GET, POST, PUT, DELETE, PATCH, etc.). In most service-virtualization scenarios, we are interested in simulating the behavior that is specific to a particular HTTP method. This scenario is about responding to the HTTP POST request, so you need to configure your stub to match on HTTP POST requests only:
|
||||
|
||||
|
||||
```
|
||||
{
|
||||
"port": 3001,
|
||||
"protocol": "http",
|
||||
"stubs": [
|
||||
{
|
||||
"predicates": [
|
||||
{
|
||||
"equals": {
|
||||
"method": "post"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This imposter defines one predicate that matches (using the keyword **equals**) on the HTTP POST request only.
|
||||
|
||||
Now take a closer look at the **endPoint** value, as defined in the implemented code:
|
||||
|
||||
|
||||
```
|
||||
`string endPoint = "http://localhost:3001/api/v1/users/login";`
|
||||
```
|
||||
|
||||
In addition to listening to port 3001 (as defined in <http://localhost:3001>), the **endPoint** is more specific, in that it expects the incoming HTTP POST request to go to the /api/v1/users/login path. How do you tell mountebank to only match exactly on the /api/v1/users/login path? By adding the path key-value pair to the stub's predicate:
|
||||
|
||||
|
||||
```
|
||||
{
|
||||
"port": 3001,
|
||||
"protocol": "http",
|
||||
"stubs": [
|
||||
{
|
||||
"predicates": [
|
||||
{
|
||||
"equals": {
|
||||
"method": "post",
|
||||
"path": "/api/v1/users/login"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This imposter now knows that HTTP requests arriving at port 3001 must be a POST method and must point at the /api/v1/users/login path. The only thing left to simulate is the expected HTTP response.
|
||||
|
||||
Add the response to the JSON imposter:
|
||||
|
||||
|
||||
```
|
||||
{
|
||||
"port": 3001,
|
||||
"protocol": "http",
|
||||
"stubs": [
|
||||
{
|
||||
"predicates": [
|
||||
{
|
||||
"equals": {
|
||||
"method": "post",
|
||||
"path": "/api/v1/users/login"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": [
|
||||
{
|
||||
"is": {
|
||||
"statusCode": 200,
|
||||
"body": "Successful login."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
With mountebank imposters, you define responses as a collection of JSON key-value pairs. In most cases, it is sufficient to simply state that a response is a **statusCode** and a **body**. This case is simulating the "happy path" response that has the status code **OK (200)** and the body containing a simple message **Successful login** (as specified in the acceptance criteria).
|
||||
|
||||
### How to run virtualized services?
|
||||
|
||||
OK, now that you have virtualized the **User authentication** service (at least its "happy path"), how do you run it?
|
||||
|
||||
Remember that you have already started mountebank, and it reported that it is running in memory as the <http://localhost> domain. Mountebank is listening on port 2525 and taking orders.
|
||||
|
||||
Great, now you have to tell mountebank that you have the imposter ready. How do you do that? Send an HTTP POST request to <http://localhost:2525/imposters>. The requests body must contain the JSON you created above. There are a few techniques available to send that request. If you're versed in [curl][9], using it to send HTTP POST requests would be the simplest, quickest way to stand up the imposter. But many people prefer a more user-friendly way to send the HTTP POST to mountebank.
|
||||
|
||||
The easy way to do that is to use [Postman][10]. If you download and install Postman, you can point it at <http://localhost:2525/imposters>, select the POST method from the pulldown menu, and copy and paste the imposter JSON into the raw body.
|
||||
|
||||
When you click Send, the imposter will be created, and you should get Status 201 (Created).
|
||||
|
||||
![Postman output][11]
|
||||
|
||||
Your virtualized service is now running! You can verify it by navigating to the **tests** folder and running the **dotnet test** command:
|
||||
|
||||
![dotnet test output][12]
|
||||
|
||||
### Conclusion
|
||||
|
||||
This demo shows how easy it is to remove blockages and control dependencies by simulating services you're dependent on. Mountebank is a fantastic tool that easily and cheaply simulates all kinds of very elaborate, sophisticated services.
|
||||
|
||||
In this installment, I just had time to illustrate how to virtualize a simple "happy path" service. If you go back to the actual user story, you will notice that its acceptance criteria contain several "less happy" paths (cases when someone is repeatedly trying to log in using invalid credentials). It's a bit trickier to properly virtualize and test those use cases, so I've left that exercise for the next installment in this series.
|
||||
|
||||
How will you use service virtualization to solve your testing needs? I would love to hear about it in the comments.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/3/service-virtualization-test-driven-development
|
||||
|
||||
作者:[Alex Bunardzic][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/alex-bunardzic
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/laptop_screen_desk_work_chat_text.png?itok=UXqIDRDD (Person using a laptop)
|
||||
[2]: https://opensource.com/article/19/8/mutation-testing-evolution-tdd
|
||||
[3]: https://opensource.com/article/19/9/mutation-testing-example-tdd
|
||||
[4]: https://opensource.com/article/20/2/automate-unit-tests
|
||||
[5]: http://www.google.com/search?q=new+msdn.microsoft.com
|
||||
[6]: https://opensource.com/sites/default/files/uploads/dotnet-test.png (Output of dotnet.test)
|
||||
[7]: http://www.mbtest.org/
|
||||
[8]: https://opensource.com/sites/default/files/uploads/mountebank-startup.png (mountebank startup)
|
||||
[9]: https://curl.haxx.se/
|
||||
[10]: https://www.postman.com/
|
||||
[11]: https://opensource.com/sites/default/files/uploads/status-201.png (Postman output)
|
||||
[12]: https://opensource.com/sites/default/files/uploads/dotnet-test2.png (dotnet test output)
|
||||
@@ -1,153 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (5 productivity apps for Linux)
|
||||
[#]: via: (https://opensource.com/article/20/3/productivity-apps-linux-elementary)
|
||||
[#]: author: (Scott Nesbitt https://opensource.com/users/scottnesbitt)
|
||||
|
||||
5 productivity apps for Linux
|
||||
======
|
||||
Get organized and accomplish more with these five productivity apps for
|
||||
the Elementary Linux desktop.
|
||||
![Person drinking a hat drink at the computer][1]
|
||||
|
||||
I've had a soft spot for [Elementary OS][2] since I first encountered it in 2013. A lot of that has to do with the distribution being very clean and simple.
|
||||
|
||||
Since 2013, I've recommended Elementary to people who I've helped [transition to Linux][3] from other operating systems. Some have stuck with it. Some who moved on to other Linux distributions told me that Elementary helped smooth the transition and gave them more confidence using Linux.
|
||||
|
||||
Like the distribution itself, many of the applications created specifically for Elementary OS are simple, clean, and useful. They can help boost your day-to-day productivity, too.
|
||||
|
||||
### About "pay-what-you-want" apps
|
||||
|
||||
Some apps in the Elementary AppCenter ask you to pay what you can. You're not obliged to pay to the full amount a developer asks for (or pay anything, for that matter). However, any money that changes hands goes to support the development of those apps.
|
||||
|
||||
Three of the applications in this article—Quilter, Notes-up, and Envelope—are pay-what-you-want. If you find an app useful, I encourage you to send some money the developer's way.
|
||||
|
||||
### Envelope
|
||||
|
||||
Managing your budget should be simple. More than a few people, though, struggle with the task. That's where [Envelope][4] can help. While Envelope doesn't pack the features of something like [GnuCash][5], it's good enough for most of us.
|
||||
|
||||
The app is built around the [envelope system][6] of personal and household budgeting. The first time you launch Envelope, you need to set up an account. You can do that manually, or you can import a [QIF][7] file containing financial information from another program.
|
||||
|
||||
![Adding an account in Envelope][8]
|
||||
|
||||
Either way, Envelope offers a set of categories (your envelopes). Add or delete categories as you see fit—for example, I don't own a car, so I deleted the Fuel category.
|
||||
|
||||
From there, add transactions. Those can be your expenses or your income. Or both.
|
||||
|
||||
![Entering a transaction in Envelope][9]
|
||||
|
||||
Envelope gives you an overview of your spending and income. To get a more focused view of your budget, you can report on the current or previous month or a specific range of dates.
|
||||
|
||||
### Notes-Up
|
||||
|
||||
[Notes-Up][10]'s look and feel are reminiscent of note-taking tools like [Standard Notes][11], Simplenote, and the macOS Notes app. If you use any of them, switching to Notes-Up will be smooth and painless. Regardless, Notes-Up is easy to learn and use.
|
||||
|
||||
![Notes-Up][12]
|
||||
|
||||
Create a note and start typing. Notes-Up supports Markdown, making it easy to add formatting to your notes.
|
||||
|
||||
![Taking notes in Notes-Up][13]
|
||||
|
||||
If your Markdown is rusty, you can click the buttons on the toolbar to add formatting like lists; bold, italics, and strikethrough; code blocks; images; and more. You can also export your notes as PDF or Markdown files.
|
||||
|
||||
Use Notes-Up for a while, and you'll wind up with a long list of notes. Organize them using _notebooks_. You can, for example, create personal, school, and work notebooks. On top of that, Notes-Up enables you to create sub-notebooks. Under my notebook for Opensource.com, for example, I have sub-notebooks for articles and the news roundups I curate.
|
||||
|
||||
Notebooks not your thing? Then use tags to add keywords to your notes to make them easier to sort.
|
||||
|
||||
### Yishu
|
||||
|
||||
I do as much of my work as I can in [plain text][14]. That includes my task list. For that, I turn to a handy command-line application called [Todo.txt][15].
|
||||
|
||||
If you aren't comfortable working at the command line, then [Yishu][16] is for you. It has Todo.txt's key features but graphically on the desktop.
|
||||
|
||||
![Yishu][17]
|
||||
|
||||
When you first fire up Yishu, it asks you to open an existing Todo.txt file. If you have one, open it. Otherwise, create a task. That also creates a new file for your tasks.
|
||||
|
||||
![Adding a task in Yishu][18]
|
||||
|
||||
Your options are limited: a description of the task and a priority. You can also add a due date in the format _YYYY-MM-DD_—for example, _2020-02-17_.
|
||||
|
||||
When you click **OK**, Yishu saves the file Todo.txt to your **/home** folder. That might not be where you want to store your tasks. You can tell Yishu to use another folder in its preferences.
|
||||
|
||||
### Reminduck
|
||||
|
||||
Chances are, your notifications and reminders are jarring. A piercing buzz, an annoying beep, a text box that appears when you least expect it. Why not add a bit of [calm][19] and a bit of whimsy to your reminders—with a duck?
|
||||
|
||||
That's the idea behind [Reminduck][20]. It's a simple and fun way to tell yourself it's time to do, well, anything.
|
||||
|
||||
Fire up the app and create a reminder. You can add a description, date, and time for the reminder to appear, and you can set it to repeat. Reminders can repeat after a number of minutes that you set or at specific times every day, week, or month.
|
||||
|
||||
![Reminduck][21]
|
||||
|
||||
You can set up more than one reminder. Reminduck organizes your reminders, and you can edit or delete them.
|
||||
|
||||
![Reminduck reminders][22]
|
||||
|
||||
When the reminder is triggered, a little message pops out of the notification area on the desktop along with a soft alert and an icon of a smiling duck.
|
||||
|
||||
![Reminduck notification][23]
|
||||
|
||||
### Quilter
|
||||
|
||||
It's easy enough to write with [Markdown][24] in a plain old text editor. Some folks, though, prefer to work with a dedicated Markdown editor. On the Elementary OS desktop, one option is [Quilter][25].
|
||||
|
||||
![Quilter][26]
|
||||
|
||||
Quilter is pretty basic. There's no toolbar to insert formatting; you have to add Markdown by hand. On the other hand, Quilter displays a running word count and an estimate of how long it will take to read what you're writing.
|
||||
|
||||
![Quilter][27]
|
||||
|
||||
The editor's options are few. There's a preview mode, and you can export your documents to PDF or HTML. The result of an export has the same look as a preview. That's not a bad thing.
|
||||
|
||||
Quilter's other options include the ability to change the line spacing and margins, set the editor's font, as well as enable syntax highlighting and spell checking. It also has a mode that you can use to focus on a single line or a single paragraph while you're writing.
|
||||
|
||||
### Final thoughts
|
||||
|
||||
Sometimes, the best tools to boost your productivity are simple ones. Applications like the five above focus on doing one thing and doing it well.
|
||||
|
||||
Envelope, Notes-Up, Yishu, Reminduck, and Quilter won't appeal to everyone. But if you use Elementary OS, give them a try. They can help you keep on track and do what you need to do.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/3/productivity-apps-linux-elementary
|
||||
|
||||
作者:[Scott Nesbitt][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/scottnesbitt
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/coffee_tea_laptop_computer_work_desk.png?itok=D5yMx_Dr (Person drinking a hat drink at the computer)
|
||||
[2]: https://elementary.io
|
||||
[3]: https://opensource.com/article/18/12/help-non-techies
|
||||
[4]: https://nlaplante.github.io/envelope/
|
||||
[5]: https://opensource.com/article/20/2/gnucash
|
||||
[6]: https://en.wikipedia.org/wiki/Envelope_system
|
||||
[7]: https://en.wikipedia.org/wiki/Quicken_Interchange_Format
|
||||
[8]: https://opensource.com/sites/default/files/uploads/envelope-add-account.png (Adding an account in Envelope)
|
||||
[9]: https://opensource.com/sites/default/files/uploads/envelope-entering-transaction.png (Entering a transaction in Envelope)
|
||||
[10]: https://appcenter.elementary.io/com.github.philip-scott.notes-up/
|
||||
[11]: https://opensource.com/article/18/12/taking-notes-standard-notes
|
||||
[12]: https://opensource.com/sites/default/files/uploads/notes-up-main-window.png (Notes-Up)
|
||||
[13]: https://opensource.com/sites/default/files/uploads/notes-up-taking-note.png (Taking notes in Notes-Up)
|
||||
[14]: https://plaintextproject.online
|
||||
[15]: https://opensource.com/article/20/1/open-source-to-do-list
|
||||
[16]: https://appcenter.elementary.io/com.github.lainsce.yishu/
|
||||
[17]: https://opensource.com/sites/default/files/uploads/yishu-task-list.png (Yishu)
|
||||
[18]: https://opensource.com/sites/default/files/uploads/yishu-add-task.png (Adding a task in Yishu)
|
||||
[19]: https://weeklymusings.net/weekly-musings-025
|
||||
[20]: https://appcenter.elementary.io/com.github.matfantinel.reminduck/
|
||||
[21]: https://opensource.com/sites/default/files/uploads/reminduck.png (Reminduck)
|
||||
[22]: https://opensource.com/sites/default/files/uploads/remiunduck-reminders-list.png (Reminduck reminders)
|
||||
[23]: https://opensource.com/sites/default/files/uploads/reminduck-notification.png (Reminduck notification)
|
||||
[24]: https://opensource.com/article/19/8/markdown-beginners-cheat-sheet
|
||||
[25]: https://appcenter.elementary.io/com.github.lainsce.quilter/
|
||||
[26]: https://opensource.com/sites/default/files/uploads/quilter.png (Quilter)
|
||||
[27]: https://opensource.com/sites/default/files/uploads/quilter-editing.png (Quilter)
|
||||
@@ -1,209 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Using the Quarkus Framework on Fedora Silverblue – Just a Quick Look)
|
||||
[#]: via: (https://fedoramagazine.org/using-the-quarkus-framework-on-fedora-silverblue-just-a-quick-look/)
|
||||
[#]: author: (Stephen Snow https://fedoramagazine.org/author/jakfrost/)
|
||||
|
||||
Using the Quarkus Framework on Fedora Silverblue – Just a Quick Look
|
||||
======
|
||||
|
||||
![Using the Quarkus Framework on Fedora Silverblue – Just a Quick Look][1]
|
||||
|
||||
[Quarkus][2] is a framework for Java development that is described on their web site as:
|
||||
|
||||
> A Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM, crafted from the best of breed Java libraries and standards
|
||||
>
|
||||
> <https://quarkus.io/> – Feb. 5, 2020
|
||||
|
||||
Silverblue — a Fedora Workstation variant with a container based workflow central to its functionality — should be an ideal host system for the Quarkus framework.
|
||||
|
||||
There are currently two ways to use Quarkus with Silverblue. It can be run in a pet container such as Toolbox/Coretoolbox. Or it can be run directly in a terminal emulator. This article will focus on the latter method.
|
||||
|
||||
### Why Quarkus
|
||||
|
||||
[According to Quarkus.io][3]: “Quarkus has been designed around a containers first philosophy. What this means in real terms is that Quarkus is optimized for low memory usage and fast startup times.” To achieve this, they employ first class support for Graal/Substrate VM, build time Metadata processing, reduction in reflection usage, and native image preboot. For details about why this matters, read [Container First][3] at Quarkus.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
A few prerequisites will need to configured before you can start using Quarkus. First, you need an IDE of your choice. Any of the popular ones will do. VIM or Emacs will work as well. The Quarkus site provides full details on how to set up the three major Java IDE’s (Eclipse, Intellij Idea, and Apache Netbeans). You will need a version of JDK installed. JDK 8, JDK 11 or any distribution of OpenJDK is fine. GrallVM 19.2.1 or 19.3.1 is needed for compiling down to native. You will also need Apache Maven 3.53+ or Gradle. This article will use Maven because that is what the author is more familiar with. Use the following command to layer Java 11 OpenJDK and Maven onto Silverblue:
|
||||
|
||||
```
|
||||
$ rpm-ostree install java-11-openjdk* maven
|
||||
```
|
||||
|
||||
Alternatively, you can download your favorite version of Java and install it directly in your home directory.
|
||||
|
||||
After rebooting, configure your _JAVA_HOME_ and _PATH_ environment variables to reference the new applications. Next, go to the [GraalVM download page][4], and get GraalVM version 19.2.1 or version 19.3.1 for Java 11 OpenJDK. Install Graal as per the instructions provided. Basically, copy and decompress the archive into a directory under your home directory, then modify the _PATH_ environment variable to include Graal. You use it as you would any JDK. So you can set it up as a platform in the IDE of your choice. Now is the time to setup the native image if you are going to use one. For more details on setting up your system to use Quarkus and the Quarkus native image, check out their [Getting Started tutorial][5]. With these parts installed and the environment setup, you can now try out Quarkus.
|
||||
|
||||
### Bootstrapping
|
||||
|
||||
Quarkus recommends you create a project using the bootstrapping method. Below are some example commands entered into a terminal emulator in the Gnome shell on Silverblue.
|
||||
|
||||
```
|
||||
$ mvn io.quarkus:quarkus-maven-plugin:1.2.1.Final:create \
|
||||
-DprojectGroupId=org.jakfrost \
|
||||
-DprojectArtifactId=silverblue-logo \
|
||||
-DclassName="org.jakfrost.quickstart.GreetingResource" \
|
||||
-Dpath="/hello"
|
||||
$ cd silverblue-logo
|
||||
```
|
||||
|
||||
The bootstrapping process shown above will create a project under the current directory with the name _silverblue-logo_. After this completes, start the application in development mode:
|
||||
|
||||
```
|
||||
$ ./mvnw compile quarkus:dev
|
||||
```
|
||||
|
||||
With the application running, check whether it responds as expected by issuing the following command:
|
||||
|
||||
```
|
||||
$ curl -w '\n' http://localhost:8080/hello
|
||||
```
|
||||
|
||||
The above command should print _hello_ on the next line. Alternatively, test the application by browsing to _<http://localhost:8080/hello>_ with your web browser. You should see the same lonely _hello_ on an otherwise empty page. Leave the application running for the next section.
|
||||
|
||||
### Injection
|
||||
|
||||
Open the project in your favorite IDE. If you are using Netbeans, simply open the project directory where the _pom.xml_ file resides. Now would be a good time to have a look at the _pom.xml_ file.
|
||||
|
||||
Quarkus uses ArC for its dependency injection. ArC is a dependency of quarkus-resteasy, so it is already part of the core Quarkus installation. Add a companion bean to the project by creating a java class in your IDE called _GreetingService.java_. Then put the following code into it:
|
||||
|
||||
```
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class GreetingService {
|
||||
|
||||
public String greeting(String name) {
|
||||
return "hello " + name;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
The above code is a verbatim copy of what is used in the injection example in the Quarkus Getting Started tutorial. Modify _GreetingResource.java_ by adding the following lines of code:
|
||||
|
||||
```
|
||||
import javax.inject.Inject;
|
||||
import org.jboss.resteasy.annotations.jaxrs.PathParam;
|
||||
|
||||
@Inject
|
||||
GreetingService service;//inject the service
|
||||
|
||||
@GET //add a getter to use the injected service
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Path("/greeting/{name}")
|
||||
public String greeting(@PathParam String name) {
|
||||
return service.greeting(name);
|
||||
}
|
||||
```
|
||||
|
||||
If you haven’t stopped the application, it will be easy to see the effect of your changes. Just enter the following _curl_ command:
|
||||
|
||||
```
|
||||
$ curl -w '\n' http://localhost:8080/hello/greeting/Silverblue
|
||||
```
|
||||
|
||||
The above command should print _hello Silverblue_ on the following line. The URL should work similarly in a web browser. There are two important things to note:
|
||||
|
||||
1. The application was running and Quarkus detected the file changes on the fly.
|
||||
2. The injection of code into the app was very easy to perform.
|
||||
|
||||
|
||||
|
||||
### The native image
|
||||
|
||||
Next, package your application as a native image that will work in a _podman_ container. Exit the application by pressing **CTRL-C**. Then use the following command to package it:
|
||||
|
||||
```
|
||||
$ ./mvnw package -Pnative -Dquarkus.native.container-runtime=podman
|
||||
```
|
||||
|
||||
Now, build the container:
|
||||
|
||||
```
|
||||
$ podman build -f src/main/docker/Dockerfile.native -t silverblue-logo/silverblue-logo
|
||||
```
|
||||
|
||||
Now run it with the following:
|
||||
|
||||
```
|
||||
$ podman run -i --rm -p 8080:8080 localhost/silverblue-logo/silverblue-logo
|
||||
```
|
||||
|
||||
To get the container build to successfully complete, it was necessary to copy the _/target_ directory and contents into the _src/main/docker/_ directory. Investigation as to the reason why is still required, and though the solution used was quick and easy, it is not an acceptable way to solve the problem.
|
||||
|
||||
Now that you have the container running with the application inside, you can use the same methods as before to verify that it is working.
|
||||
|
||||
Point your browser to the URL <http://localhost:8080/> and you should get a _index.html_ that is automatically generated by Quarkus every time you create or modify an application. It resides in the _src/main/resources/META-INF/resources/_ directory. Drop other HTML files in this _resources_ directory to have Quarkus serve them on request.
|
||||
|
||||
For example, create a file named _logo.html_ in the _resources_ directory containing the below markup:
|
||||
|
||||
```
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
To change this license header, choose License Headers in Project Properties.
|
||||
To change this template file, choose Tools | Templates
|
||||
and open the template in the editor.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Silverblue</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<img src="fedora-silverblue-logo.png" alt="Fedora Silverblue"/>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Next, save the below image alongside the _logo.html_ file with the name _fedora-silverblue-logo.png_:
|
||||
|
||||
![][6]
|
||||
|
||||
Now view the results at <http://localhost:8080/logo.html>.
|
||||
|
||||
#### Testing your application
|
||||
|
||||
Quarkus supports junit 5 tests. Look at your project’s _pom.xml_ file. In it you should see two test dependencies. The generated project will contain a simple test, named _GreetingResourceTest.java_. Testing for the native file is only supported in _prod_ mode. However, you can test the _jar_ file in _dev_ mode. These tests are RestAssured, but you can use whatever test library you wish with Quarkus. Use Maven to run the tests:
|
||||
|
||||
```
|
||||
$ ./mvnw test
|
||||
```
|
||||
|
||||
More details can be found in the Quarkus [Getting Started][7] tutorial.
|
||||
|
||||
#### Further reading and tutorials
|
||||
|
||||
Quarkus has an extensive collection of [tutorials and guides][8]. They are well worth the time to delve into the breadth of this microservices framework.
|
||||
|
||||
Quarkus also maintains a [publications][9] page that lists some very interesting articles on actual use cases of Quarkus. This article has only just scratched the surface of the topic. If what was presented here has piqued your interest, then follow the above links for more information.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/using-the-quarkus-framework-on-fedora-silverblue-just-a-quick-look/
|
||||
|
||||
作者:[Stephen Snow][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/jakfrost/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2020/02/quarkus-816x345.jpg
|
||||
[2]: https://quarkus.io/
|
||||
[3]: https://quarkus.io/vision/container-first
|
||||
[4]: https://www.graalvm.org/downloads/
|
||||
[5]: https://quarkus.io/get-started/
|
||||
[6]: https://fedoramagazine.org/wp-content/uploads/2020/02/fedora-silverblue-logo.png
|
||||
[7]: https://quarkus.io/guides/getting-started
|
||||
[8]: https://quarkus.io/guides/
|
||||
[9]: https://quarkus.io/publications/
|
||||
@@ -1,472 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to set up the Raspberry Pi Zero for travel)
|
||||
[#]: via: (https://opensource.com/article/20/3/raspberry-pi-zero)
|
||||
[#]: author: (Peter Garner https://opensource.com/users/petergarner)
|
||||
|
||||
How to set up the Raspberry Pi Zero for travel
|
||||
======
|
||||
You don't have to invest large amounts of money to build a relatively
|
||||
powerful system that can be taken on the road and used productively.
|
||||
![Airplane flying with a globe background][1]
|
||||
|
||||
For some time now, I've been a huge fan of the [Raspberry Pi][2] computer in all of its various forms. I have a number of them, and each has a server role to play. Most of the time, they work extremely well, and I'm safe in the knowledge that the small amount of power they consume is keeping the bills down.
|
||||
|
||||
If you've read my blog before, you may have read my account of how I [migrated my desktop computing][3] to a Pi 3. This worked well for quite a while, but I finally had to accept that editing large graphics and multimedia files was a problem, so I replaced it with an [Intel NUC][4]. My hankering for Pi experimentation was still there, though, and I decided to do a "what-if" exercise and see if it could survive on the road. And that's when I dragged my Pi Zero out of retirement from my spares box.
|
||||
|
||||
### Why travel with a Raspberry Pi
|
||||
|
||||
_"Why would I want to do this? Surely the trend is to travel with as powerful a device as possible?"_
|
||||
|
||||
Well, it's like this. Last year, my employer issued a decree that in order to conform to its security policy, we would no longer be able to check laptops in as luggage, and not long after, the US government decided to summarily ban carrying laptops in hand baggage to and from certain countries, y'know, for security. So how do we get around that one? The sensible option would be to not travel with a laptop and use a hot-desk style spare at the destination. After all, everything is in the cloud now, right? Or, you could carry your important, commercially sensitive data on a CD/DVD or memory stick, but only if it's encrypted to a standard and your employer's data handling policy allows that.
|
||||
|
||||
The problem is multi-faceted though: What if your role is such that you need to be able to fix software/systems on-the-go and you don't always have access to a "spare" laptop? On the occasions when I travel (by train), I have to lug my laptop with me, and it's a pain over the course of a day. Then there's the loss/theft/damage problem. Laptops can be easy targets, they can get left on trains, or you could be mugged. Or there's the "[evil maid][5]" scenario, in which someone interferes with your device without your knowledge. The list goes on.
|
||||
|
||||
So, here's what you can get with a Pi Zero portable computer:
|
||||
|
||||
* It's small enough to fit in hand baggage or your pocket.
|
||||
* It's cheap enough at $8 to buy another if yours gets lost/stolen/damaged.
|
||||
* The entire OS and data are held on a "disk" that is as small as a fingernail, is cheap, and is easily bought in a wide variety of retail outlets. If need be, you can create a new one from a borrowed card from a phone.
|
||||
* A full development environment with the ability to work offline or online. It can also act as an SSH server so that more than one person can use it at once.
|
||||
* Safe storage: If you are paranoid or traveling on certain airlines, you can remove the "disk" and store it in your wallet or on your person. If your computer is stolen in transit, go and buy another one off the shelf: you're already set up with the OS.
|
||||
* Network-tolerant: Around the world, there are country-specific WiFi frequencies, and a simple text-file change enables you to be compliant within minutes.
|
||||
* Keyboard independent: You can use a compliant Bluetooth keyboard, but when you need to do something more demanding, you can just plug any USB keyboard into the spare USB connector using an On-The-Go cable.
|
||||
* Power supply tolerant: My 3300mAh power bank can run the Pi Zero for about eight hours, but if all else fails, you can use a TV's USB connector to power it. Generally speaking, if a TV has HDMI, it will also have a USB socket, and the Zero only draws about 120mA. Or use someone's phone charger!
|
||||
* Finally, if you're unfortunate enough to lose/damage your "disk," you can easily create another by downloading your 2GB image from a secure location in the cloud and burning it to a new card. Try doing _that_ with a normal laptop.
|
||||
|
||||
|
||||
|
||||
That's motivation enough for me!
|
||||
|
||||
Here is my finished product, with a beer coaster for scale.
|
||||
|
||||
![Pi Zero W setup][6]
|
||||
|
||||
### How I set up the Pi Zero for travel
|
||||
|
||||
The cheap-as-chips Pi Zero has always been a bit of an odd beast, in my opinion. It features a single-core CPU and has a frugal 512MB of memory, some of which it has to share with the onboard video. The Raspbian OS currently based on Debian Stretch is touted as being suitable for the Zero with the LXDE-based "Pixel" GUI desktop, and indeed it can be loaded and started—but in reality, the poor thing really struggles to manage a GUI _and_ run useful software.
|
||||
|
||||
Nevertheless, I was determined to give it a good try and set it up with the apps that have the smallest memory footprint. I'd already been around this loop with the Pi 3, so it was more of the same—only even more so! Bearing in mind this was to be a road warrior's computer, here's what I wanted to have on it:
|
||||
|
||||
Web browser | Lightweight but with privacy in mind
|
||||
---|---
|
||||
Email | IMAP-capable and seamlessly supporting GPG
|
||||
XMPP/Jabber client | No-nonsense messaging
|
||||
VPN client | I'm on the road, remember
|
||||
Tor client | Always useful...
|
||||
Music | I carry a few MP3s, but internet radio is cool, too
|
||||
Multiple desktops | Useful with a small screen
|
||||
Editor/IDE | Hey, it's a working computer!
|
||||
FTP/SFTP client | Hey, it's a working computer!
|
||||
|
||||
All in all, it's a very useful bundle for my purposes, and if I achieve a balanced environment, it could actually work.
|
||||
|
||||
### Hardware
|
||||
|
||||
The hardware was a bit of a challenge. The battery was not a problem, as I have a variety of rechargeable power packs with varying capacity, so it is really just a question of picking a suitable one for the day. The only prerequisite was that the battery should be able to take a charge while being used, and all my Jackery brand batteries do this.
|
||||
|
||||
For my "disk," I opted for my in-house standard 32GB SanDisk Extreme microSDHC. They're very reliable, and the size is big enough to hold a lot of software and data while still remaining affordable.
|
||||
|
||||
The video output would, I anticipated, be HDMI-out using the Zero's mini-HDMI connector. This suited my purposes well, as the majority of hotel TVs use this interface. Audio would also go via HDMI.
|
||||
|
||||
That left the human interface devices (HID), and this, predictably, caused the most consternation. I hate Bluetooth with a passion, and with the Zero's limited connectors, I'd have to bite the bullet and use a Bluetooth keyboard and mouse, preferably a combined one—and small. There's no point in having a tiny computer if you have lug a great honking keyboard around as well, so my unhappy quest began.
|
||||
|
||||
### Bluetooth woes
|
||||
|
||||
The Zero has a limited number of USB connectors on board—just one, if you allow for the power connector, which obviously means you have limited connection options. You could always use a USB extender hub, but then that's more to carry—including another power supply. That basically leaves you trying to connect a keyboard and mouse via Bluetooth. I don't believe the hype about how it can "_easily connect a wide variety of devices together,_" and I wrestle with a variety of allegedly standard devices trying to get the bloody things to play nicely together. Part of the problem with the Pi (I think) is that there's some unintended interaction between WiFi and Bluetooth that causes weird stuff to happen. My problem was that I was able to connect to the Pi using a keyboard _or_ a mouse, but not both reliably at the same time. And yes, I have a variety of allegedly standards-following Bluetooth devices, none of which decided to work together properly.
|
||||
|
||||
At this point, I was wondering if there was a Bluetooth Samaritan's Helpline, but there wasn't, so what the heck was I going to do?! Temporarily, I resorted to using an Apple USB keyboard with two USB sockets; this was useful for setup but not for being on the road. In the end, I spent hours browsing eBay and Amazon, and then I found it: a "_3- in-1 Mini Wireless Bluetooth Keyboard Mouse Touchpad For Windows iOS Android UK - Backlit, Ultra-thin, Built-in Rechargeable Battery, QWERTY_." Perfect on paper, but would it work? I sent off the money, and four days later, a slim package arrived from China. And it bloody well worked!! First time, every time, it got on with the Pi like pie and chips. I promptly ordered another one—accidents happen, and I wouldn't want to be left in the lurch.
|
||||
|
||||
So, with my hardware lineup complete, I could settle down to setting up the Pi proper.
|
||||
|
||||
### The GUI and the Pi
|
||||
|
||||
Going back to my software requirements, I thought long and hard about the smallest desktop environment and went with the supplied LXDE desktop, which I'd used several times on other projects. Although the Pi struggled a bit with screen handling, it generally performed well, and I started setting up my software.
|
||||
|
||||
Web browser 1
|
||||
|
||||
Midori: a good compromise between size and modernity. Supports private browsing and is bundled with the Pi
|
||||
|
||||
Web browser 2
|
||||
|
||||
Links2 in graphic mode: lightweight, fast, secure, works with proxies
|
||||
|
||||
Email
|
||||
|
||||
Sylpheed: small, light, and works well with GPG
|
||||
|
||||
XMPP/Jabber client
|
||||
|
||||
Profanity. It's great!
|
||||
|
||||
VPN client
|
||||
|
||||
OpenVPN
|
||||
|
||||
Tor client
|
||||
|
||||
Ha! Links2 again
|
||||
|
||||
Music
|
||||
|
||||
SMPlayer: the GUI for MPlayer
|
||||
|
||||
Editor/IDE
|
||||
|
||||
Geany: small and light but powerful
|
||||
|
||||
Image viewer/editor
|
||||
|
||||
Pinta
|
||||
|
||||
FTP/SFTP client
|
||||
|
||||
Filezilla
|
||||
|
||||
**Other stuff:**
|
||||
|
||||
Midnight Commander
|
||||
|
||||
Not just a file manager (more later)
|
||||
|
||||
Tor proxy server
|
||||
|
||||
Always on, so I can use Tor as needed
|
||||
|
||||
Nmap
|
||||
|
||||
I sometimes need to test stuff
|
||||
|
||||
vnStat
|
||||
|
||||
Monitor data usage on the wlan0 interface
|
||||
|
||||
SSH/SFTP server
|
||||
|
||||
Standard issue on this distro
|
||||
|
||||
UFW
|
||||
|
||||
Firewall; can't be too careful!
|
||||
|
||||
Gopher client
|
||||
|
||||
Gopherspace! It's still out there, and I use the _original_ Gopher client!
|
||||
|
||||
All of the above are tried, tested, and very stable packages. The web browser decision was a calculated one: the Zero doesn't _really_ have what it takes to negotiate a modern ~~bloated, ad-ridden~~ website, and honestly, I have a phone that can handle that sort of thing. Likewise, the decision to run a Tor proxy: it's very handy to be able to access Marianas Web, and using Links2 as a browser means that the risk is minimal.
|
||||
|
||||
Sylpheed is a mature package that I believe has largely been replaced by Claws but is actually less demanding of resources. GPG integrated seamlessly with it, and I was able to exchange signed/encrypted messages with ease. It renders both plain-text and HTML messages well, and the interface is uncluttered.
|
||||
|
||||
I needed a _simple_ XMPP/Jabber client. The problem I've found with many apps of this type is that they try to incorporate multiple messaging standards when I only really need XMPP. Profanity is ideal as it does one job and does it very well.
|
||||
|
||||
### System setup
|
||||
|
||||
I spent a considerable amount of time setting up the OS for the best performance; a task made easier because I have set up numerous Raspberry Pis before. In such a small (as in memory) system, the decision to use a swap file was not taken lightly, and unfortunately, using a GUI desktop meant that the swap is in frequent use. The alternative is to not have one and hope that the system doesn't freeze up too much. I went with the suggested default of 100MB.
|
||||
|
||||
I then looked at logging. As this was supposed to be a small, portable system, I didn't see much point in having extensive logging, especially as it would have a negative effect on the SDHC card in the long term. One solution was a combination of disabling logging in apps wherever possible and sacrificing a little memory to create a tmpfs in-memory filesystem. This would have the added advantage that it would be recreated on each boot. So, I worked out that 8MB could be used for this and duly updated **/etc/fstab**. This works extremely well.
|
||||
|
||||
|
||||
```
|
||||
`tmpfs /var/log tmpfs defaults,noatime,nosuid,mode=0755,size=8m 0 0`
|
||||
```
|
||||
|
||||
I also had to update **/etc/rc.local** to provide some essential directories on startup; this kept the rsyslog and Tor daemons happy.
|
||||
|
||||
|
||||
```
|
||||
mkdir -p /var/log/rsyslog
|
||||
mkdir -p /var/log/tor/
|
||||
mkdir -p /var/log/vnstat/
|
||||
chown vnstat.root /var/log/vnstat/
|
||||
chown debian-tor /var/log/tor/
|
||||
```
|
||||
|
||||
With all that in place, the little computer was almost ready for Prime Time. But there was a problem. I've already mentioned the frugal amount of memory on the Zero, and even with the GUI and apps pared down to the bone, I was regularly using swap space. To make matters worse, much worse, my carefully set up desktop menus were having problems.
|
||||
|
||||
The Pi desktop comes with an _incredible_ amount of software installed, mainly to satisfy its original purpose as an educational machine. I decided early on to edit the menus to remove a lot of the "junk" and replace it with my list of apps, so I fired up the Main Menu Editor app. Normally this is quite responsive, but on the resource-challenged Zero, it had worrying lags and pauses as I made changes. Ultimately, it meant that my ad-hoc menus were corrupted, and worse still, the default Pi menus had been reinstated. Searching for help with this problem revealed that the menu system is convoluted, and if an update is not saved properly, the defaults will be substituted.
|
||||
|
||||
I looked at the structure of the menus as best I could and decided that trying to pick the frigging things apart was more trouble than it was worth. So, I ditched LXDE/Pixel and installed XFCE (and its even lighter-weight GUI) in its place. This time, the menu editor seemed more stable, but as I made the changes, I realized that yes, it was happening again. At that point, I had a tantrum and threw my toys out of the pram. I'd reached a crossroads in my road-warrior setup: it worked very well apart from the menus, and I felt I couldn't go back after all that work, so, with a heavy heart, I had a drastic rethink. It was supposed to be a hacker's machine, right? It was Linux and, to many like-minded people, that meant a text-based interface, right?
|
||||
|
||||
So I did what I had to do: I ditched the accursed GUI! Yes, go back to the '90s, maybe even earlier, and Run it Like a Boss.™ The main problem I had in my mind was that I'd no longer have nice, safe, icon-driven apps and multiple desktops to work with. My WiFi/network switcher would be no more, and worse still, I'd have to try and manage Bluetooth from the command line. That was going to be a major challenge, but I decided to proceed anyway, and I had a cloned copy of my microSDHC just in case I lost my nerve.
|
||||
|
||||
### Set it up again
|
||||
|
||||
Incidentally, if I were using a full-spec Raspberry Pi 3, I wouldn't have been in this situation. But it was my choice, so… This exercise started out well. I was already rebooting into the terminal login prompt, and the Bluetooth keyboard was working, so I was able to log in (previously, I'd run **startx** to get to the desktop). Since I no longer had the desktop bloat to worry about, my memory usage was a mere 78MB and no swap usage; I felt better already. But what about the apps that make life easy? I did more hunting around, and here's what I came up with.
|
||||
|
||||
Web browser
|
||||
|
||||
Links2 _not_ in graphic mode: lightweight, fast, secure, works with proxies
|
||||
|
||||
Email
|
||||
|
||||
(Neo)Mutt: powerful, extensible and works well with GPG
|
||||
|
||||
XMPP/Jabber client
|
||||
|
||||
Profanity. It's great!
|
||||
|
||||
VPN client
|
||||
|
||||
OpenVPN
|
||||
|
||||
Tor client
|
||||
|
||||
Ha! Links2 again
|
||||
|
||||
Music
|
||||
|
||||
Midnight Commander + mpg123
|
||||
|
||||
Editor/IDE
|
||||
|
||||
Nano: I'm using it to write this
|
||||
|
||||
FTP/SFTP client
|
||||
|
||||
Midnight Commander
|
||||
|
||||
File manager
|
||||
|
||||
Midnight Commander
|
||||
|
||||
**Other stuff**
|
||||
|
||||
Tor proxy server
|
||||
|
||||
Always on, so I can use Tor as needed
|
||||
|
||||
Nmap
|
||||
|
||||
I sometimes need to test stuff
|
||||
|
||||
vnStat
|
||||
|
||||
Monitor data usage on the wlan0 interface
|
||||
|
||||
SSH/SFTP server
|
||||
|
||||
Standard issue on this distro
|
||||
|
||||
Gopher client
|
||||
|
||||
Gopherspace! It's still out there, and I use the _original_ Gopher client!
|
||||
|
||||
**Graphics workarounds**
|
||||
|
||||
fbcat
|
||||
|
||||
Takes a screenshot using the framebuffer device
|
||||
|
||||
fbi
|
||||
|
||||
Linux framebuffer imageviewer: displays images in a terminal
|
||||
|
||||
fbgs
|
||||
|
||||
Displays PostScript/PDF files using the Linux framebuffer device on a terminal
|
||||
|
||||
pnmtopng
|
||||
|
||||
Converts a PPM into a PNG file
|
||||
|
||||
You'll notice that there's not too much change there, with a few notable exceptions to display graphical content.
|
||||
|
||||
### Do it again, and do it properly
|
||||
|
||||
Things looked good, but I still had some issues to solve.
|
||||
|
||||
#### Desktops
|
||||
|
||||
"But what about the multiple desktops?!" I hear you asking, "How will you view images or connect to WiFi networks?" I needed to find a solution, and fast. For the multiple desktop thing, I decided to install tmux, the Linux Terminal Multiplexer. There are so many good reasons to run tmux on a Linux system, but my key reasons are that it makes multi-screen working possible, and it uses very little memory. It also enables me to connect to the Pi via SSH and take over the session, as you can see below.
|
||||
|
||||
![tmux running on Raspberry Pi][7]
|
||||
|
||||
#### Music
|
||||
|
||||
I must say that it's very important to me to be able to listen to music while I'm working or relaxing, so the loss of (S)MPlayer was a major blow. I was able to listen to single MP3s or complete playlists. I was able to stream internet radio. Sigh. Midnight Commander came to the rescue with its ability to handle various file types. The secret is in the Extension File menu, which looks like this for me:
|
||||
|
||||
|
||||
```
|
||||
shell/i/.mp3
|
||||
Open=/usr/lib/mc/ext.d/sound.sh open mp3
|
||||
regex/i/\\.(m3u|pls)$
|
||||
Open=/usr/lib/mc/ext.d/sound.sh open playlist
|
||||
```
|
||||
|
||||
And my sound.sh looks like this:
|
||||
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
do_open_action() {
|
||||
filetype=$1
|
||||
case "${filetype}" in
|
||||
playlist)
|
||||
mpg123 -C -@ "${MC_EXT_FILENAME}"
|
||||
;;
|
||||
m3u)
|
||||
mpg123 -C -@ "${MC_EXT_FILENAME}"
|
||||
;;
|
||||
mp3)
|
||||
mpg123 -C "${MC_EXT_FILENAME}"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
case "${action}" in
|
||||
open)
|
||||
("${MC_XDG_OPEN}" "${MC_EXT_FILENAME}" >/dev/null 2>&1) || \
|
||||
do_open_action "${filetype}"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
Tapping Enter on an MP3 will play the file, or tapping on an M3U playlist will play whatever's in the playlist. I used the **-C** option so that I could have access to mpg123's controls. Sorted!
|
||||
|
||||
#### SFTP/FTP clients
|
||||
|
||||
Midnight Commander again! You can set up a client connection entry with the built-in menu and use **CTL+\** to select it from a drop-down. The FTP site is rendered in one of the panels as a directory structure, and you can just treat it as you would a local filesystem. SFTP is a bit harder, as you have to set it up as an SSH connection and then copy as required; it's OK if you have password authentication, but for public key authentication, I found it less involved to use SFTP from the command line. For reference, this is the syntax:
|
||||
|
||||
|
||||
```
|
||||
sftp://[user@]machine:[port]/[remote-dir]
|
||||
|
||||
The user, port and remote-dir elements are optional.
|
||||
```
|
||||
|
||||
![MC FTP client example][8]
|
||||
|
||||
#### WiFi selector
|
||||
|
||||
In the GUI world, I had had a nice, icon-based WiFi network manager app that I could use to switch between networks. The text-mode alternative is the bizarrely named wicd-curses*.* It's an app that communicates with wicd (wireless control daemon) using cursor keys, and it works very well. I had to disable the dhcpcd service using systemctl to get it to work, but at least it lets me select the appropriate network, including my home network or my phone's wireless hotspot when I'm out on the road. Here's [how to do it][9].
|
||||
|
||||
![wicd-curses WiFi app][10]
|
||||
|
||||
#### Email and web browsing
|
||||
|
||||
For email and web browsing, I use Mutt (Neomutt) and Links2, respectively, and they just work.
|
||||
|
||||
#### Gopher
|
||||
|
||||
Trust me; you're not old enough to remember Gopher, the text mode forerunner of the WWW. Strictly speaking, it was before my time as well, but I run a thriving Gopher server, so I need a client. Here's what a Gopher server looks like running on a Raspberry Pi.
|
||||
|
||||
|
||||
```
|
||||
gopher://gopher.petergarner.net:70
|
||||
|
||||
__/\\\\\\\\\\\\\\\\\\_______/\\\\\\\\\\\\\\\\\\\\\\\\\\__________________/\\\\\\____
|
||||
_/\\\\\///////\\\\\\____\/\\\\\/////////\\\\\\______________/\\\\\\\\\\____
|
||||
_\/\\\\\\_____\/\\\\\\___\/\\\\\\_______\/\\\\\\__/\\\\\\_______/\\\\\/\\\\\\____
|
||||
_\/\\\\\\\\\\\\\\\\\\\\\/____\/\\\\\\\\\\\\\\\\\\\\\\\\\/___\///______/\\\\\/\/\\\\\\____
|
||||
_\/\\\\\//////\\\\\\____\/\\\\\/////////_____/\\\\\\_____/\\\\\/__\/\\\\\\____
|
||||
_\/\\\\\\____\//\\\\\\___\/\\\\\\_____________\/\\\\\\____/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\_
|
||||
_\/\\\\\\_____\//\\\\\\__\/\\\\\\_____________\/\\\\\\\\__///////////\\\\\//__
|
||||
_\/\\\\\\______\//\\\\\\_\/\\\\\\_____________\/\\\\\\_______________\/\\\\\\___
|
||||
_\///________\///__\///______________\///_________________\///___
|
||||
|
||||
Welcome to... "The Rpi4 Gopher"
|
||||
... your source for local information, and beyond!
|
||||
|
||||
--> [14] About this server (and legal)/
|
||||
|
||||
-- Content
|
||||
|
||||
[18] Tech-related/
|
||||
[19] Politics and Propaganda (from all sides)/
|
||||
[20] Cyber and Internet related/
|
||||
[21] Stuff (filed under "miscellany")/
|
||||
.....
|
||||
```
|
||||
|
||||
### Performance
|
||||
|
||||
Overall, I'm pleased to say that my switch to text mode has been very beneficial with userland tasks handled responsively. As you can see from a typical **top** display, there's plenty of available and cached memory remaining from the original 512MB. Right now, I'm listening to a playlist, writing this article in an SSH-connected tmux session, running top**,** and Mutt is handling emails.
|
||||
|
||||
On a regular basis, that's all I'll probably need to do, but it's great to have the option to develop and test software on the go, if I need to. Here's a simple Python script to get the Pi's CPU serial number (type):
|
||||
|
||||
|
||||
```
|
||||
>>> #!/usr/bin/env python
|
||||
...
|
||||
>>> import subprocess
|
||||
>>>
|
||||
>>> def GetCPUserial():
|
||||
... cpuinfo = subprocess.check_output(["/bin/cat", "/proc/cpuinfo"])
|
||||
... cpuinfo = cpuinfo.replace("\t","")
|
||||
... cpuinfo = cpuinfo.split("\n")
|
||||
... [ legend, cpuserial ] = cpuinfo[11].split(' ')
|
||||
... return cpuserial
|
||||
...
|
||||
>>> print GetCPUserial()
|
||||
9000c1
|
||||
>>>
|
||||
|
||||
[/code] [code]
|
||||
|
||||
top - 15:55:47 up 5:49, 6 users, load average: 0.21, 0.25, 0.34
|
||||
Tasks: 112 total, 1 running, 110 sleeping, 1 stopped, 0 zombie
|
||||
%Cpu(s): 3.9 us, 5.8 sy, 0.0 ni, 90.3 id, 0.0 wa, 0.0 hi, 0.0 si,
|
||||
|
||||
KiB Mem : 493252 total, 37984 free, 73248 used, 382020 buff/cache
|
||||
KiB Swap: 102396 total, 102392 free, 4 used. 367336 avail Mem
|
||||
```
|
||||
|
||||
I take regular backups, of course, using the indispensable [rpi-clone][11]. The beauty of this app is that I can copy the entire microSDHC card to another while the Pi is running. It also has the advantage that if I use a smaller capacity card, it will automatically and safely take this into account. I use a 32GB card, but I can dump it to an 8GB card because I'm only using about 4GB. The converse is true for a larger destination card. If you add only one utility to your Pi, this should be it—it's saved me more grief than _anything_ I've ever used! You can also use it to create distribution copies of your system.
|
||||
|
||||
#### Battery life
|
||||
|
||||
I can only describe the battery life as "impressive." Once the Zero is booted up, it takes an average 0.15A / 0.65W with the following running:
|
||||
|
||||
* System
|
||||
* Bluetooth
|
||||
* WiFi
|
||||
* Audio (HDMI) subsystem
|
||||
* Video (HDMI) subsystem
|
||||
|
||||
|
||||
|
||||
I've measured 20 hours of mixed usage from my 6000mAh Jackery Jetpack power bank, and if I disable WiFi and just use it in "local" mode, possibly more. It's certainly practical to use it with my smaller, 3300mAh Anker battery, which unfortunately isn't rechargeable while in use. I also have a 20,000mAh battery for long trips: I have yet to try that out.
|
||||
|
||||
### Taking it on the road
|
||||
|
||||
In terms of computing as a challenge, setting up the Zero has been a really valuable exercise. It's taught me to become acquainted with the operating system and app software at a low level in order to squeeze the maximum amount of memory from the system. It's also taught me that I don't have to invest large amounts of money to build a relatively powerful system that can be taken on the road and used productively.
|
||||
|
||||
Now that I've almost finished setting it up, it's time to actually _take_ it on the road and see how it runs in the field. Hey, maybe I'll actually take into a field and see if I can do some work and listen to music.
|
||||
|
||||
* * *
|
||||
|
||||
_This was originally published on [Peter Garner's blog][12] under a CC BY-NC-ND 4.0 license and is reused here with the author's permission._
|
||||
|
||||
Having recently co-authored a book about building things with the Raspberry Pi ( Raspberry Pi Hacks...
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/3/raspberry-pi-zero
|
||||
|
||||
作者:[Peter Garner][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/petergarner
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/plane_travel_world_international.png?itok=jG3sYPty (Airplane flying with a globe background)
|
||||
[2]: https://opensource.com/resources/raspberry-pi
|
||||
[3]: https://www.petergarner.net/projects/Mac_Mini_to_rpi-part_1.pdf
|
||||
[4]: https://en.wikipedia.org/wiki/Next_Unit_of_Computing
|
||||
[5]: http://threatbrief.com/evil-maid-attack/
|
||||
[6]: https://opensource.com/sites/default/files/uploads/pi-zero-on-the-road.jpg (Pi Zero W setup)
|
||||
[7]: https://opensource.com/sites/default/files/uploads/tmux-on-pi.png (tmux running on Raspberry Pi)
|
||||
[8]: https://opensource.com/sites/default/files/uploads/ftp-client-mc.png (MC FTP client example)
|
||||
[9]: https://www.raspberrypi.org/forums/viewtopic.php?t=150124#p987430
|
||||
[10]: https://opensource.com/sites/default/files/uploads/wifi-selector.png (wicd-curses WiFi app)
|
||||
[11]: https://github.com/billw2/rpi-clone
|
||||
[12]: https://petergarner.net/notes/index.php?thisnote=20180202-Travels%20with%20a%20Pi
|
||||
@@ -1,118 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to whiteboard collaboratively with Drawpile)
|
||||
[#]: via: (https://opensource.com/article/20/3/drawpile)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
How to whiteboard collaboratively with Drawpile
|
||||
======
|
||||
Need to whiteboard or draw something with others? Give Drawpile a try.
|
||||
![markers for a whiteboard][1]
|
||||
|
||||
Thanks to applications like [Krita][2] and [MyPaint][3], open source software users have all the tools they need to create stunning digital paintings. They are so good that you can see [art created with Krita][4] in some of your [favorite RPG books][5]. And it's getting better all the time; for example, [GIMP][6] 2.10 adopted MyPaint's brush engine, so users can benefit from MyPaint without even installing the whole application.
|
||||
|
||||
But what about collaborative illustration? What do two or more artists do when they want to work together on one piece? What does your work team use when you need to whiteboard during a business meeting? Those are the questions, and the answer is [Drawpile][7].
|
||||
|
||||
![Drawpile's UI][8]
|
||||
|
||||
Nyarlathotep by Sophia Eberhard
|
||||
|
||||
Drawpile is a drawing application for Linux, Windows, and macOS. It's got a respectable brush engine and all the basic editorial tools (selection tools, flips and flops, mirror, and so on) to make it a good freehand digital paint application. But its most powerful feature is its easy multi-user mode. If you have Drawpile installed, you can host a drawing session from your computer or on a Drawpile server, allowing other users to join you in your virtual studio. This goes well beyond a screen-share session, which would just allow other users to _view_ your painting, and it's not a remote desktop with just one cursor. Drawpile enables several users, each with their own brush, to work on the same canvas at the same time over a network that can span the globe.
|
||||
|
||||
### Installing Drawpile
|
||||
|
||||
If you're using Linux, Drawpile is available as a [Flatpak][9] from [Flathub.org][10].
|
||||
|
||||
On Windows and macOS, download and install Drawpile from [Drawpile's download page][11]. When you first launch it on macOS, you must right-click on its icon and select **Open** to accept that it hasn't been signed by a registered Apple developer.
|
||||
|
||||
### Drawing with Drawpile
|
||||
|
||||
The Drawpile interface is simple and minimal. Along the right side of the application window are docked palettes, and along the top is a toolbar. Most of the tools available to you are visible: paint brushes, paint buckets, lines, Bézier curves, and so on.
|
||||
|
||||
For quick access to brushes, Drawpile allows you to assign a unique brush, along with all of its settings (including color), to the number keys **1** through **5** on your keyboard. It's an efficient and easy way to quickly move between drawing tools. The **6** key holds an eraser.
|
||||
|
||||
Drawpile also has layers, so you can keep different parts of your painting separate until you combine them for your final render. If you're an animator, you can even use Drawpile's onion skin and flipbook features (both available in the **Layer** menu) to do rudimentary frame-by-frame animation. Unlike Krita, Drawpile doesn't feature an [animation timeline][12], but it's enough for quick and fun animations.
|
||||
|
||||
### Custom brushes
|
||||
|
||||
Drawpile isn't Krita or MyPaint, so its brush engine is simple in comparison. The preset brushes have the usual properties, though, including:
|
||||
|
||||
* **Opacity** adjusts how your strokes blend with existing paint
|
||||
* **Hardness** defines the edges of your stroke
|
||||
* **Smudging** allows existing strokes to be affected by your brush
|
||||
* **Color pickup** allows your paint to pick up color from existing strokes
|
||||
* **Spacing** controls how often the full brush cursor is sampled during a stroke
|
||||
|
||||
|
||||
|
||||
Most of these are pressure-sensitive, so if you're using a drawing tablet (Wacom, for instance), then your brush strokes are dynamic depending upon pen pressure. The tablet support is borrowed from Krita, and it makes a big difference (although it's probably overkill for mock-ups or whiteboarding sessions).
|
||||
|
||||
When you find a brush setting you like, you can add it to your brush set so you can use it again later. To add a brush, click the **Menu** button in the top-right corner of the docked **Brushes** palette and select **Add brush**.
|
||||
|
||||
![Adding a brush in Drawpile][13]
|
||||
|
||||
If the **Brushes** palette isn't visible, go to the **View** menu in the top menu bar and select **Brushes** from the **Docks** submenu.
|
||||
|
||||
### Collaborative drawing
|
||||
|
||||
To participate in a shared drawing session, go to the **Session** menu and click either **Host** to host a session or **Join** to join in on an existing one.
|
||||
|
||||
#### Hosting a session
|
||||
|
||||
If you're hosting a session, give your session a title and an optional password (or leave it blank to allow anyone in). In the **Server** section, set whether you're hosting the session from your computer or from someone else's server. You can host sessions on **pub.drawpile.net** for free, but all of your data will be sent out to the internet, which could affect performance. If you have a good internet connection, the lag is negligible, but if you're not confident in your internet speed or there's no reason to go out to the internet because your collaborators are in the same building as you, then you can host your session locally.
|
||||
|
||||
![Settings for hosting a session][14]
|
||||
|
||||
If you host locally, you must provide your IP address or computer name (ending in **.local**) to your collaborators so their Drawpile apps can find your computer. You can find your computer name in the **Sharing** preferences of the GNOME desktop if you're on Linux:
|
||||
|
||||
![Sharing Drawpile in GNOME][15]
|
||||
|
||||
You must enable Remote Login, and possibly adjust your [firewall settings][16] to allow other users to get through.
|
||||
|
||||
On macOS and Windows, you may be running a firewall, and you may need to provide additional sharing permissions in your control panel or system settings.
|
||||
|
||||
#### Joining a session
|
||||
|
||||
If you're joining a session, you need to know either the URL or the IP address of the session you're trying to join. A URL is like a website address, such as syntheticdreams.net/listing. An IP address is the numerical version of a URL, such as 93.184.216.34. Some IP addresses are internal to your building, while others exist out on the internet. If you haven't been invited to a drawing session, you might be able to find a public group on Drawpile's [Communities][17] page.
|
||||
|
||||
### Drawing with friends
|
||||
|
||||
Open source has always been about sharing. Drawpile is not only software you can share with your friends and colleagues; it's software that allows you to work with them in a fun and creative way. Try Drawpile for your next project or boardroom meeting!
|
||||
|
||||
Nick Hamilton talks about what he loves about the open source digital painting tool, Krita, prior...
|
||||
|
||||
Akkana Peck shares three of her favorite GIMP tools.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/3/drawpile
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/markers_whiteboard_draw.png?itok=hp6v1gHC (markers for a whiteboard)
|
||||
[2]: https://krita.org/en/
|
||||
[3]: http://mypaint.org
|
||||
[4]: https://krita.org/en/item/interview-with-alexandru-sabo/
|
||||
[5]: https://paizo.com/products/btpy9g9x?Pathfinder-Roleplaying-Game-Bestiary-5
|
||||
[6]: https://www.gimp.org/
|
||||
[7]: https://drawpile.net
|
||||
[8]: https://opensource.com/sites/default/files/uploads/drawpile-ui.jpg (Drawpile's UI)
|
||||
[9]: https://opensource.com/article/19/10/how-build-flatpak-packaging
|
||||
[10]: https://flathub.org/apps/details/net.drawpile.drawpile
|
||||
[11]: https://drawpile.net/download/
|
||||
[12]: https://opensource.com/life/16/10/animation-krita
|
||||
[13]: https://opensource.com/sites/default/files/uploads/drawpile-brush-add.jpg (Adding a brush in Drawpile)
|
||||
[14]: https://opensource.com/sites/default/files/uploads/drawpile-session-host.png (Settings for hosting a session)
|
||||
[15]: https://opensource.com/sites/default/files/uploads/gnome-sharing.png (Sharing Drawpile in GNOME)
|
||||
[16]: https://opensource.com/article/19/7/make-linux-stronger-firewalls
|
||||
[17]: https://drawpile.net/communities
|
||||
@@ -1,281 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Adding a display to a travel-ready Raspberry Pi Zero)
|
||||
[#]: via: (https://opensource.com/article/20/3/pi-zero-display)
|
||||
[#]: author: (Peter Garner https://opensource.com/users/petergarner)
|
||||
|
||||
Adding a display to a travel-ready Raspberry Pi Zero
|
||||
======
|
||||
A small eInk display turns a Raspberry Pi into a self-contained,
|
||||
pocket-sized travel computer.
|
||||
![Pi Zero][1]
|
||||
|
||||
In my earlier article, I explained how I [transformed a Raspberry Pi Zero][2] into a minimal, portable, go-anywhere computer system that, although small, can actually achieve useful things. I've since made iterations that have proved interesting and made the little Pi even more useful. Read on to learn what I've done.
|
||||
|
||||
### After the road trip
|
||||
|
||||
My initial Pi Zero setup [proved its worth][3] on a road trip to Whitby, but afterward, it was largely consigned to the "pending" shelf, waiting for another assignment. It was powered up weekly to apply updates, but other than that, it was idle. Then one day, as I was flicking through emails from various Pi suppliers, I came across a (slightly) reduced e-Ink display offer: hmmm… and there was a version for the Pi Zero as well. What could I do with one?
|
||||
|
||||
ModMyPi was selling a rather neat [display and driver board combination][4] and a [small case][5] with a transparent window on top. I read the usual reviews, and apart from one comment about the _boards being a very tight fit_, it sounded positive. I ordered it, and it turned up a few days later. I had noted from the product description that the display board didn't have GPIO headers installed, so I ordered a Pi Zero WH (wireless + headers pre-installed) to save me the bother of soldering one on.
|
||||
|
||||
### Some assembly required
|
||||
|
||||
As with most of these things, some self-assembly was required, so I carefully opened the boxes and laid out the parts on the desk. The case was nicely made apart from ridiculous slots for a watch strap (?!) and some strange holes in the side to allow tiny fingers to press the five I/O buttons on the display. "_Could I get a top without holes?"_ I inquired on the review page. "_No."_ Okay then.
|
||||
|
||||
With the case unpacked, it was time to open the display box. A nicely designed board was first out, and there were clear instructions on the Pi-Supply website. The display was so thin (0.95mm) that I nearly threw it out with the bubble wrap.
|
||||
|
||||
The first job was to mount the display board on the Pi Zero. I checked to make sure I could attach the display cable to the driver board when it was joined to the Pi and decided that, with my sausage fingers, I'd attach the display first and leave it flapping in the breeze while I attached the driver board to the Pi. I carefully got the boards lined up on the GPIO pins, and, with those in place, I folded over the display "screen" to sit on top of the board. With the piggy-backed boards in place, I then _verrrry_ carefully shoe-horned the assembly into place in the case. Tight fit? Yeah, you're not kidding, but I got it all safely in place and snapped the top on, and nothing appeared to be broken. Phew!
|
||||
|
||||
### How to set up your display
|
||||
|
||||
I'm going to skip a chunk of messing about here and refer you to the maker's [instructions][6] instead. Suffice to say that after a few installs, reboots, and coffees, I managed to get a working e-Ink display! Now all I had to do was figure out what to do with it.
|
||||
|
||||
One of the main challenges of working with a small device like [my "TravelPi"][2] is that you don't have access to as much screen real estate as you would on a larger machine. I like the size and power of the device though, so it's really a compromise as to what you get out of it. For example, there's a single screen accessible via the HDMI port, and I've used tmux to split that into four separate, usable panes. If I really need to view something else urgently, I could always **Ctrl+Z** into another prompt and do the necessary configs, but that's messy.
|
||||
|
||||
I wanted to see various settings and maybe look at some system settings, and the e-Ink display enabled me to do all that! As you can see from the image below, I ended up with a very usable info panel that is updated by a simple(-ish) Python script (**qv**) either manually or by a crontab entry every 10 minutes. The manufacturer states that the update frequency should be "no more than 1Hz if you want your display to last for a long time." Ten minutes is fine, thank you.
|
||||
|
||||
Here's what I wanted to be able to see at a glance:
|
||||
|
||||
Hostname | And device serial number
|
||||
---|---
|
||||
IP address | Current internal IP address
|
||||
VPN status | Inactive/country/IP address
|
||||
Tor status | Inactive/IP address
|
||||
"Usage" | Percentage disk space and memory used
|
||||
Uptime | So satisfying to see those long uptimes
|
||||
|
||||
And here it is: a display that's the same size as the Pi Zero and 1" deep.
|
||||
|
||||
![PiZero Display][7]
|
||||
|
||||
### How to populate the display
|
||||
|
||||
Now I needed to populate the display. As seems to be the norm these days, the e-Ink support software is in Python, which, of course, is installed as standard with most Linux distros. _Disclaimer:_ Python is not my first (dev) language, but the code below works for me. It'll probably work for you, too.
|
||||
|
||||
|
||||
```
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import datetime
|
||||
import socket
|
||||
import netifaces as ni
|
||||
import psutil
|
||||
import subprocess
|
||||
|
||||
from netifaces import AF_INET, AF_INET6, AF_LINK, AF_PACKET
|
||||
from papirus import PapirusText, PapirusTextPos, Papirus
|
||||
from subprocess import check_output
|
||||
from datetime import timedelta
|
||||
|
||||
rot = 0
|
||||
screen = Papirus(rotation = rot)
|
||||
fbold = '/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf'
|
||||
fnorm = '/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf'
|
||||
text = PapirusTextPos(rotation = rot)
|
||||
|
||||
def GetBootTime():
|
||||
return datetime.datetime.fromtimestamp(psutil.boot_time())
|
||||
|
||||
def GetUptime():
|
||||
with open('/proc/uptime','r') as f:
|
||||
uptime_seconds = float(f.readline().split()[0])
|
||||
u = str(timedelta(seconds = uptime_seconds))
|
||||
duration,junk = u.split(".")
|
||||
hr,mi,sc = duration.split(":")
|
||||
return "%sh %sm %ss" % ( hr,mi,sc )
|
||||
|
||||
def getHostname():
|
||||
hostname = socket.gethostname()
|
||||
return hostname
|
||||
|
||||
def getWiFiIPaddress():
|
||||
try:
|
||||
ni.interfaces()
|
||||
[ 'wlan0', ]
|
||||
return ni.ifaddresses('wlan0')[AF_INET][0]['addr']
|
||||
except:
|
||||
return 'inactive'
|
||||
|
||||
def getVPNIPaddress():
|
||||
try:
|
||||
ni.interfaces()
|
||||
[ 'tun0', ]
|
||||
return ni.ifaddresses('tun0')[AF_INET][0]['addr']
|
||||
except:
|
||||
return 'inactive'
|
||||
|
||||
def GetTmuxEnv():
|
||||
if 'TMUX_PANE' in os.environ:
|
||||
return ' (t)'
|
||||
return ' '
|
||||
|
||||
def GetCPUserial():
|
||||
cpuinfo = subprocess.check_output(["/bin/cat", "/proc/cpuinfo"])
|
||||
cpuinfo = cpuinfo.replace("\t","")
|
||||
cpuinfo = cpuinfo.split("\n")
|
||||
[ legend, cpuserial ] = cpuinfo[12].split(' ')
|
||||
cpuserial = cpuserial.lstrip("0")
|
||||
return cpuserial
|
||||
|
||||
def GetMemUsed():
|
||||
memUsed = psutil.virtual_memory()[2]
|
||||
return memUsed
|
||||
|
||||
def GetDiskUsed():
|
||||
diskUsed = psutil.disk_usage('/')[3]
|
||||
return diskUsed
|
||||
|
||||
def CheckTor():
|
||||
try:
|
||||
TS = "active: pid %s" %check_output(['pidof','tor'])
|
||||
except:
|
||||
TS = 'inactive'
|
||||
return TS
|
||||
|
||||
def CheckVPN():
|
||||
return VPNlo
|
||||
# ---------------------------------------------------------------------------
|
||||
def main():
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
VPNlo = 'inactive'
|
||||
|
||||
if (len(sys.argv) == 2):
|
||||
try:
|
||||
VPNlo = sys.argv[1]
|
||||
except:
|
||||
VPNlo = 'inactive'
|
||||
|
||||
text = PapirusTextPos(False,rotation=rot)
|
||||
text.AddText("%s %s %s"% (getHostname(),GetCPUserial(),GetTmuxEnv()),x=1,y=0,size=12,invert=True,fontPath=fbold)
|
||||
text.AddText("IP %s" % getWiFiIPaddress(),x=1,y=16,size=12,fontPath=fnorm)
|
||||
if ( getVPNIPaddress() == 'inactive' ):
|
||||
text.AddText("VPN %s" % CheckVPN(),x=1,y=30,size=12,fontPath=fnorm)
|
||||
else:
|
||||
text.AddText("VPN %s" % getVPNIPaddress(),x=1,y=30,size=12,fontPath=fnorm)
|
||||
text.AddText("TOR %s" % CheckTor(),x=1,y=44,size=12,fontPath=fnorm)
|
||||
text.AddText("MEM %s% DISK %s% used" % (GetMemUsed(),GetDiskUsed()),x=1,y=58,size=12,fontPath=fnorm,maxLines=1)
|
||||
text.AddText("UPTIME %s" % GetUptime(),x=1,y=72,size=12,fontPath=fnorm)
|
||||
text.WriteAll()
|
||||
|
||||
sys.exit(0)
|
||||
```
|
||||
|
||||
Normally, the script runs without any arguments and is called by a series of Bash scripts that I've written to start up various subsystems; these are, in turn, called from a menu system written in Whiptail, which is pretty versatile. In the case of the VPN system, I have a list of access points to choose from and that update the location on the display. Initially, I call the display updater with the location name (e.g., Honolulu), but at that point, I can't display the VPN IP address because I don't know it:
|
||||
|
||||
|
||||
```
|
||||
dispupdate.py ${accesspoint}
|
||||
openvpn --config $PATH/Privacy-${accesspoint}.conf --auth-user-pass credfile
|
||||
```
|
||||
|
||||
When the display updater runs again (outside the VPN startup script), the IP address is readable from the **tun0** interface and the display is updated with the IP address. I may change this later, but it works fine now. I use the **PapirusTextPos** function (rather than **PapirusText**), as this allows multiple lines to be written before the display is updated, leading to a much faster write. The **text.WriteAll()** function does the actual update.
|
||||
|
||||
### Adding more software
|
||||
|
||||
I was very pleased with my initial choice of applications, but since I'd managed to slim the whole installation down to 1.7GB, I had plenty of available space. So, I decided to see if there was anything else that could be useful. Here's what I added:
|
||||
|
||||
Irssi | IRC client
|
||||
---|---
|
||||
FreeBSD games | There are still many text-mode games to enjoy
|
||||
nmon | A _very_ comprehensive top-alike utility for all aspects of the system
|
||||
Newsbeuter | Text-mode Atom/RSS feed reader
|
||||
|
||||
And I still have about 300MB free space to take me up to 2GB, so I may add more.
|
||||
|
||||
### We keed to talk about ~~Kevin~~ Bluetooth
|
||||
|
||||
Observant readers will remember my hatred for Bluetooth and trying to pair terminal-based software with a Bluetooth device. When I bought a new Pi, I realized that I had to pair the damn thing up with the keyboards again. Oh, woe is me! But a search-engine session and a calming coffee enabled me to actually do it! It goes something like this:
|
||||
|
||||
|
||||
```
|
||||
sudo su
|
||||
bluetoothctl {enter}
|
||||
|
||||
[bluetooth]#
|
||||
|
||||
[bluetooth]# scan on
|
||||
Discovery started
|
||||
[CHG] Controller B8:27:EB:XX:XX:XX Discovering: yes
|
||||
|
||||
[bluetooth]# agent on
|
||||
Agent registered
|
||||
[NEW] Device B2:2B:XX:XX:XX:XX Bluetooth Keyboard
|
||||
Attempting to pair with B2:2B:XX:XX:XX:XX
|
||||
[CHG] Device B2:2B:XX:XX:XX:XX Connected: yes
|
||||
[agent] PIN code: 834652
|
||||
[CHG] Device B2:2B:XX:XX:XX:XX Modalias: usb:v05ACp0220d0001
|
||||
[CHG] Device B2:2B:XX:XX:XX:XX UUIDs: zzzzz
|
||||
[CHG] Device B2:2B:XX:XX:XX:XX UUIDs: yyyyy
|
||||
[CHG] Device B2:2B:XX:XX:XX:XX ServicesResolved: yes
|
||||
[CHG] Device B2:2B:XX:XX:XX:XX Paired: yes
|
||||
Pairing successful
|
||||
[CHG] Device B2:2B:XX:XX:XX:XX ServicesResolved: no
|
||||
[CHG] Device B2:2B:XX:XX:XX:XX Connected: no
|
||||
|
||||
[bluetooth]# trust B2:2B:XX:XX:XX:XX
|
||||
[CHG] Device B2:2B:XX:XX:XX:XX Trusted: yes
|
||||
Changing B2:2B:XX:XX:XX:XX trust succeeded
|
||||
[CHG] Device B2:2B:XX:XX:XX:XX RSSI: -53
|
||||
|
||||
[bluetooth]# scan off
|
||||
[CHG] Device B2:2B:XX:XX:XX:XX RSSI is nil
|
||||
Discovery stopped
|
||||
[CHG] Controller B8:27:EB:XX:XX:XX Discovering: no
|
||||
|
||||
[bluetooth]# exit
|
||||
Agent unregistered
|
||||
|
||||
$
|
||||
```
|
||||
|
||||
I was gobsmacked! No, really. I paired my other keyboard and am now considering pairing a speaker, but we'll see. I had a beer that night to celebrate my new-found "l33t" tech skills! Here is an [excellent guide][8] on how to do it.
|
||||
|
||||
### One more hardware mod
|
||||
|
||||
Until recently, I've been using as large a good-quality microSDHC card as I could afford, and in case of problems, I created a backup copy using the rsync-based rpi-clone. However, after reading various articles on the 'net where people complain about corrupted cards due to power problems, unclean shutdowns, and other mishaps, I decided to invest in a higher-quality card that hopefully will survive all this and more. This is important if you're traveling long distances and _really_ need your software to work at the destination.
|
||||
|
||||
After a long search, I found the [ATP Industrial-Grade MicroSD/MicroSDHC][9] cards, which are rated military-spec for demanding applications. That sounded perfect. However, with quality comes a cost, as well as (in this case) limited capacity. In order to keep my wallet happy, I limited myself to an 8GB card, which may not sound like a lot for a working computer, but bearing in mind I have a genuine 5.3GB of that 8GB free, it works just fine. I also have a level of reassurance that bigger but lower-quality cards can't give me, and I can create an ISO of that card that's small enough to email if need be. Result!
|
||||
|
||||
### What's next?
|
||||
|
||||
The Zero goes from strength to strength, only needing to go out more. I've gone technically about as far as I can for now, and any other changes will be small and incremental.
|
||||
|
||||
* * *
|
||||
|
||||
_This was originally published on [Peter Garner's blog][10] under a CC BY-NC-ND 4.0 and is reused here with the author's permission._
|
||||
|
||||
The new issue of the official Raspberry Pi magazine, The MagPi, comes with a free computer stuck to...
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/3/pi-zero-display
|
||||
|
||||
作者:[Peter Garner][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/petergarner
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/zero-osdc-lead.png?itok=bK70ON2W (Pi Zero)
|
||||
[2]: https://opensource.com/article/20/3/raspberry-pi-zero-w-road
|
||||
[3]: https://petergarner.net/notes/index.php?thisnote=20180511-Travels+with+a+Pi+%282%29
|
||||
[4]: https://www.modmypi.com/raspberry-pi/screens-and-displays/epaper/papirus-zero-epaper--eink-screen-phat-for-pi-zero-medium
|
||||
[5]: https://www.modmypi.com/raspberry-pi/cases-183/accessories-1125/watch-straps/pi-supply-papirus-zero-case
|
||||
[6]: https://github.com/PiSupply/PaPiRus
|
||||
[7]: https://opensource.com/sites/default/files/uploads/pizerodisplay.jpg (PiZero Display)
|
||||
[8]: https://www.sigmdel.ca/michel/ha/rpi/bluetooth_01_en.html
|
||||
[9]: https://www.digikey.com/en/product-highlight/a/atp/industrial-grade-microsd-microsdhc-cards
|
||||
[10]: https://petergarner.net/notes/index.php?thisnote=20190205-Travels+with+a+Pi+%283%29
|
||||
@@ -1,184 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How I migrated from a Mac Mini to a Raspberry Pi)
|
||||
[#]: via: (https://opensource.com/article/20/3/mac-raspberry-pi)
|
||||
[#]: author: (Peter Garner https://opensource.com/users/petergarner)
|
||||
|
||||
How I migrated from a Mac Mini to a Raspberry Pi
|
||||
======
|
||||
Learn more about Linux by turning a Raspberry Pi Model 2 into a workable
|
||||
desktop computer.
|
||||
![Vector, generic Raspberry Pi board][1]
|
||||
|
||||
Some time ago, I decided to move my computing environment from a Mac Mini PowerPC to a Raspberry Pi Model 2. This article describes my reasons for doing so and how I did it. While it is quite technical in places, if you're considering switching from an existing system to something decidedly lean and mean, there are things that you need to know before making that leap. There are lots of links to click as well, which will lead you to the software and apps that I mention.
|
||||
|
||||
Enjoy!
|
||||
|
||||
## Saying goodbye to the Mac
|
||||
|
||||
I have to admit, I've never really been an Apple fanboi, especially following a short (and ultimately unsatisfactory) fling with a plastic polycarbonate MacBook back in 2006. Although it was beautifully designed, and the software "Just Worked," I was understandably upset when it decided to expire shortly after the warranty period expired (design faults, apparently). Ah well.
|
||||
|
||||
I swore never to "invest" in an Apple machine again—until I discovered a used Mac Mini PowerPC on eBay that could be had for around $100 in 2012. It was new back in 2005 but had apparently been refurbished. "What have I got to lose, especially at that price?" I asked myself. Nobody answered, so I placed a last-minute bid, won it, and invested about the same sum of money again in bumping the memory up to 1GB and buying the OS on DVD. The OS X version was 10.4.7 Tiger, and the architecture was Power PC. It was sedate but reliable, and I was happy. It didn't take a lot of power either; some 60 watts at full load, so that was a bonus. I spent many happy hours tinkering with it and trying to find software that was supported on a device that old.
|
||||
|
||||
Predictably though, as my computing requirements grew and the Mac got older, it started to get noticeably slower, and I was aware that even simple tasks—such as asking it to run a web browser and display an HTTPS page—were causing it problems. When I finally managed to find antivirus software for it, I became aware of just how noisy the Mini's cooling fan was as the CPU struggled with the extra load.
|
||||
|
||||
A quick check of the performance monitors revealed thousands of memory-paging faults, and I realized that my old friend was soon destined for the knackers yard. Of course, that meant searching for a replacement, and that's when the fun started.
|
||||
|
||||
## A(nother) small computer
|
||||
|
||||
My main problem was that I didn't have a big budget. I looked at eBay again and found a number of Mac Minis for sale, all around the $500 mark, and many of those were early basic-spec Intel units that, like my old Mac, people had simply grown out of. Essentially, I wanted something like the old Mini, ideally with similar power consumption. A new one was out of the question, obviously.
|
||||
|
||||
Let me state that my computer requirements are pretty undemanding, and for photo/graphics work, I have another computer that consumes power like there's no tomorrow and gives off enough heat to keep me warm in winter. And then I got to thinking about the [Raspberry Pi Model 2][2]. Now before you laugh, I have around six of the things running various servers, and they do just fine. One runs a small web server, another runs a mail server, and so on. Each one costs around $30, and most use a cheap microSDHC card, so if one fails, I can easily swap it out for another, and I can usually buy a suitable card at a local supermarket—try doing that when your laptop drive fails! I also have a Netgear ReadyNAS 102 with a couple of 2TB hard drives to act as my bulk storage.
|
||||
|
||||
Suddenly, my plan looked as though it might be viable after all!
|
||||
|
||||
## Spec'ing it out
|
||||
|
||||
The specification was a bit of a no-brainer: The Model 2 Pi comes with 1GB of memory standard, the Ethernet runs at 100Mbps maximum, the clock speed is 900MHz, there are four USB ports, and that's yer lot, mate. You can overclock it, but I've never wanted to try this for various reasons.
|
||||
|
||||
I had a Pi in my spares drawer, so no problem there. I ordered a posh aluminum case made by [Flirc][3] that was on offer for $20 and duly slotted in the Pi. The power supply unit (PSU) had to be a genuine two-amp device, and again, I had a spare lying around. If you take your Pi ownership seriously, I recommend the [Anker 40W][4] five-port desktop charger: it has intelligent power management, and I'm running five Pis from one unit. Incidentally, if you inadvertently use a PSU that can't deliver the required current, you'll keep seeing a square, multi-colored icon in the top-right corner of your screen, so be warned.
|
||||
|
||||
The microSDHC "disk" was more of an issue, though. I always use SanDisk, and this time I wanted something fast, especially as this was to be a "desktop" machine. In the end, I went for a [SanDisk 8GB Extreme Pro UHS-1][5] card that promised up to 90 to 95 Mbps write/read performance. "8GB? That's not a lot of space," I hear you Windows users cry, and because this is Linux, there doesn't need to be.
|
||||
|
||||
The way I envisioned it, I'd set up the Pi normally and use it primarily as a boot disk. I'd host all my documents and media files on the network-attached storage (NAS) box, and all would be well. The NAS shares would be accessed via network filesystem (NFS), and I'd just mount them as directories on the Pi.
|
||||
|
||||
Quite early on, I elected to move my entire home directory onto the NAS, and this has worked well, with some quirks. The problem I faced was a Pi quirk, and although I was sure there was a fix, I wanted to get it up and running before the Mac finally crapped out. When the Pi boots, it seems to enable the networking part quite late in the sequence, and I found that I couldn't do my NFS mounts because the networking interface hadn't come up yet. Rather than hack around with tricky scripts, I decided to simply mount the NFS shares by hand after I'd logged in after a successful boot. This seemed to work, and it's the solution I'm using now. Now that I had a basic strategy, it was time to implement it on the "live" machine.
|
||||
|
||||
That's the beauty of working with the Raspberry Pi—you can quickly hack together a testbed and have a system up and running in under 30 minutes.
|
||||
|
||||
Regarding video, I bought an HDMI-to-DVI cable to use with my Dell monitor, and in GUI desktop mode, this comes up as 1280x1024—plenty good enough for my use. If you have a monster flat-screen TV, you can always use that instead.
|
||||
|
||||
## My software environment
|
||||
|
||||
### Operating system
|
||||
|
||||
I ultimately decided on [Arch Linux for ARM][6] 7H as the operating system. I'm a [Raspbian][7] veteran, but I didn't need the educational software that comes with it (I have other Pis for that). Arch provides a minimal environment but is full-featured, well-supported, and powerful; it also has bucket-loads of software available. After its initial installation, I'd used just over 1.2GB of space, and even now, with all my software on the microSDHC, I'm only using 2.8GB of my 8GB card. Please note that the Pi 2 is officially Arch Linux ARM 7, not 6.
|
||||
|
||||
### Desktop
|
||||
|
||||
I wanted a graphical desktop environment (even though I'm a command-line sorta guy), but it needed to be in keeping with the lean and mean ethos. I'd used [LXDE][8] before and was happy with it, so I installed it; GNOME and KDE were just too big.
|
||||
|
||||
### Web browser
|
||||
|
||||
The web browser was a bit of a problem, but after trying the default Midori, Epiphany, and a couple of others, I decided on [Firefox][9]. It's a bit flabby, but it follows standards well, and if you're going to digitally sign LibreOffice ODT documents, you'll need it anyway. One problem on a machine of this power is the tremendous toll that web-based ads place on the overall memory usage. In fact, a badly ad'ed page can make the browser stop completely, so I had to make those ads disappear. One way would be to install an ad-blocker plugin, but that's another hit on available memory, so a simpler method was called for.
|
||||
|
||||
As this is a Linux box, I simply downloaded an [ad-blocking hosts file][10]. This is an amazing piece of community work that consists of over 15,000 hostnames for basically any server that spits out ads. All the entries point to an IP address of 0.0.0.0, so there's no time wasted and your bandwidth's your own again. It's a free download and can be added to the end of an existing hosts file. Of course, the major value, as far as I'm concerned, is that page load times are much quicker.
|
||||
|
||||
The screen capture below shows an ad-free Firefox overlaid with the same page in [ELinks][11].
|
||||
|
||||
![Firefox and eLinks browsers on Raspberry Pi][12]
|
||||
|
||||
No ads in either, but if you don't need all the eye candy rendered by Firefox, ELinks will provide a super-clean experience. (Normally, all that whitespace in the Firefox image is filled with ads.) The ELinks browser is an interesting hybrid browser that is primarily text-based and is similar to the classic pure-text Lynx browser.
|
||||
|
||||
### Messaging
|
||||
|
||||
It would be overkill, and undesirable from a security point of view, to have Microsoft Skype on the Pi, so I decided on a Jabber/XMPP client, [Psi][13]. Psi has the advantage of not having a multitude of dependencies, and it also works really well. It's easy to take part in multi-user chats, and I have another Pi hosting a Jabber server to test it on. There's no character-mode version, unfortunately, and most of the text-based clients I tried had problems, so it's a GUI-only situation at the moment. No matter; it works well and doesn't use a lot of resources.
|
||||
|
||||
### Email
|
||||
|
||||
I also tried a number of email applications: this was easily the most important application. Eventually, I chose [Claws Mail][14]. Sadly, it doesn't do HTML mail, but it's rock-solid reliable. I have to say that I can't get the GNU Privacy Guard (GPG) plugin working properly yet due to some unresolved version issues, but I can always encrypt messages in a terminal, if need be.
|
||||
|
||||
### Audio
|
||||
|
||||
Music is important to me, and I chose [SMPlayer][15] as my media player. It supports many options, including playlists for local and networked files and internet radio streaming. It does the job well.
|
||||
|
||||
### Video
|
||||
|
||||
I'll not go into the video player in any great detail. Bearing in mind the hardware specs of the Pi, reliably playing back a video stream, even on the same network, was problematic. I decided that if I wanted to watch videos, I had other devices more suited to it. I did try and experiment with the **gpu_mem** setting in the **[/boot/config.txt][16]**, switching it from the default 64MB to 96MB. I was prepared to borrow a bit of application memory for the video player, but even that didn't seem to make it work well. In the end, I kept that setting so that the desktop environment would run more smoothly, and so far, I haven't had problems. The irony of this is that I have another Pi that has a [DLNA][17] server installed, and this can stream video exceedingly well—not just to one client, but several. In its defense, though, it doesn't have a desktop environment to contend with. So, for now, I don't bother trying to play video.
|
||||
|
||||
### Image processing
|
||||
|
||||
I need to do simple, lightweight photo and image editing, and I knew from prior experience that GIMP and similar packages would bring the Pi to its knees. I found an app called [Pinta][18], which resembles an enhanced Microsoft Paint, but with more cojones. As someone with a large image collection, I also needed a slideshow application. After much evaluation, I decided on [feh][19]. Normally run from a terminal within the GUI desktop, it has an incredible array of options that can help you produce an image slideshow, and again, it has low memory requirements.
|
||||
|
||||
### Office suite
|
||||
|
||||
And then there was an office suite. On the old Mac Mini, I was happily (and legally) running a copy of Microsoft Mac Office 2004, and I was truly sorry to lose that. I just needed a Microsoft Word and Excel equivalent, but I had to bear in mind the Pi's limitations. Sure, there are standalone versions of word-processor and spreadsheet applications, but there was nothing that really gave me confidence that I could edit a full-featured document.
|
||||
|
||||
I already knew of [LibreOffice][20], but I had my doubts about it because of its Java Runtime Environment (JRE) requirement, or so I thought. Thankfully, JRE was optional, and as long as I didn't want to use (database) connection pooling of macros, there was no need to enable it. I also used as many built-in options as possible, rejecting skins and themes; this brought the overall memory footprint down to a reasonable level, and hey, I'm writing this on LibreOffice Writer now! I adopted the attitude that if it has a built-in theme, use it!
|
||||
|
||||
Here's the current [memory overview][21] (in MB) from within the GUI desktop:
|
||||
|
||||
![Raspberry Pi GUI memory usage][22]
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
Other desktop software I've installed (not much as I wanted in order to keep this a minimal installation) is:
|
||||
|
||||
* [FileZilla][23]: SFTP/FTP client
|
||||
* [PuTTY][24]: SSH/telnet terminal frontend
|
||||
* [Mousepad][25]: A versatile plain-text editor, similar to Wordpad or Notepad, but much more powerful **[Note: this link was broken. Is this ok?]**
|
||||
|
||||
|
||||
|
||||
Overall, the entire setup works as intended. I've found that it performs well, if a little slow sometimes, but this is to be expected, as it's running on a Raspberry Pi with a 900MHz clock speed and 1GB of memory. As long you're aware of and prepared to accept the limitations, you can have a cheap, very functional system that doesn't take up all your desk space.
|
||||
|
||||
## Lacking in characters
|
||||
|
||||
Life with a Pi desktop is not all about the GUI; it's a very competent command-line environment too, should you need one. As a Linux developer and geek, I am very comfortable in a character-mode environment, and this is where the Pi really comes into its own. The performance you can expect in the command-line environment, at least in my configuration, is dependent on a number of factors. I'm limited to a certain extent by the Pi's network-interface speed and the overall performance of my Netgear ReadyNAS 102, another slightly underpowered, consumer-grade ARM box. The one thing that did please me, though, was the noticeable increase in speed over the Mac Mini!
|
||||
|
||||
Running in a native terminal environment, this is the typical memory usage (in MB) you might expect:
|
||||
|
||||
![Raspberry Pi terminal memory usage][26]
|
||||
|
||||
One thing to note is the lack of a swap partition. It's generally accepted that any type of swap system on a Raspberry Pi is a Very Bad Thing™ and will wear out your SD card in no time. I considered setting up a swap partition on the NAS box, but I ruled this out early on, as it would very negatively impact the network as a whole, and as with the NFS mount issue, the swap partition would need to be mounted before the network came up. So no go.
|
||||
|
||||
Having lived with Raspberry Pis for some time now, let's just say that one has to learn to set things up carefully in the first place to avoid the need, and ultimately, it can teach you to manage computers better.
|
||||
|
||||
As part of my efforts to make the Pi as useful as possible, I had to envision a scenario where whatever I was working on was either so resource-hungry that I couldn't run a GUI desktop or the GUI was just not required. That meant reproducing as many of the desktop-only apps in a character-mode environment. In fact, this was easier than finding the equivalent desktop apps.
|
||||
|
||||
Here is my current lineup:
|
||||
|
||||
* **File manager:** [Midnight Commander][27]; if you're old enough to remember Norton Commander, you'll know what it looks like.
|
||||
* **File transfer:** SSH/SFTP; normally handled by PuTTY and FileZilla on the desktop, you just use these two commands as provided.
|
||||
* **Web browser:** Lynx or Links are classic character-mode browsers that significantly speed up the internet experience.
|
||||
* **Music player:** Yes, you can play music in a character-mode terminal! [Mpg123][28] is the name of the app, and when it's run as **mpg123 -C**, it allows full keyboard control of all playback functions. If you want to be really cool, you can alter the way Midnight Commander handles MP3 files by editing **/etc/mc/mc.ext** and adding the code snippet below. This allows you to browse and play your music collection with ease. [code] shell/i/.mp3
|
||||
Open=/usr/bin/mpg123 -C %f
|
||||
View=%view{ascii} /usr/lib/mc/ext.d/sound.sh view mp3
|
||||
```
|
||||
* **Office:** Don't be silly! Oh wait, though; I installed the character-mode spreadsheet app called **sc** (Supercalc?), and there's always Vi if you want to edit a text document, but don't expect to able to edit any Microsoft files. If your need is truly great, you can install a supplementary application called Antiword, which will let you view a .doc file.
|
||||
* **Email:** A bit of a problem, as the Claws Mail mailbox format is not directly compatible with my character-mode app of choice, Mutt. There's a workaround, but I'm only going to do it if I get some spare time. For sending quick emails, I installed ssmtp, which is described as "a send-only sendmail emulator for machines which normally pick their mail up from a centralized mail hub." The setup is minimal, and overhead is practically nil, as normally it's invoked only when mail is being sent. So, you can do things like typing **echo "The donuts are on my desk" | mail -s"Important News" [everybody@myoffice.com][29]** from the command line without firing up a GUI mail app.
|
||||
|
||||
|
||||
|
||||
For everything else, it's just a question of flipping back to the GUI desktop. Speaking of which…
|
||||
|
||||
![Raspberry Pi GUI desktop environment][30]
|
||||
|
||||
Quite a busy screen, but the Raspberry Pi handles it well. Here, I'm using LibreOffice to write this article, there's a network status box, Firefox is on the mpg123 website, and there's a terminal running top showing how much memory (isn't) being used. The drop-down menu on the left shows the office suite apps.
|
||||
|
||||
## Other scenarios and thoughts
|
||||
|
||||
### What's where
|
||||
|
||||
With any hybrid system like this, it's important to remember what is located where so that, in the event of any problems, recovery will be easier. In my current configuration, the microSDHC card contains only the operating system, and as much as possible, any system-configuration files are also on there. Your own userland data will be on the NAS in your home directory. Ideally, you should be to replace or update the software on the microSDHC without having any adverse effects on your computing environment as a whole, but in IT, it's never that straightforward.
|
||||
|
||||
In the X11 GUI desktop system, although there is a default config file in **/etc/X11**, you will invariably have a customized version containing your own preferences. (This is by design.) Your own file on the NAS, however, will reference files on the microSDHC:
|
||||
|
||||
![Location of files][31]
|
||||
|
||||
The overall effect is that if you change one environment for another, you will invariably experience a change (or loss) in functionality. Hopefully, the changes will be minor, but you do need to be aware of the sometimes ambiguous links.
|
||||
|
||||
Please remember that the **root** user will _always_ be on the microSDHC, and if your NAS box fails for any reason, you'll still be able to boot your system and at least do some recovery work.
|
||||
|
||||
### NAS alternatives
|
||||
|
||||
While I'm in my home office, I have full access to my NAS box, which represents what (in today's terminology) would be a personal cloud. I much prefer this solution to a commercial cloud that is invariably managed by a company of unknown origin, location, security, and motives. For those reasons, I will always host my data where I can see it and physically get to it as required. Having said that, you may not be as paranoid as I am and will want to hook up your Pi desktop to an external cloud share.
|
||||
|
||||
In that case, using an NFS mount as a basis for your home directory should mean that it's simply a matter of editing your **/etc/fstab** to point the NFS client at a different location. In my setup, the NAS box is called, er, NASBOX, and the local NFS share mountpoint is called **/NASmount**. When you create your non-root user, you'll simply move their home directory to an existing directory called **/NASmount**:
|
||||
```
|
||||
|
||||
|
||||
NASBOX:/data/yourshare /NASmount nfs
|
||||
nfsvers=3,rsize=8192,wsize=8192,timeo=60,intr,auto 0 0
|
||||
|
||||
mount -t nfs -v NASBOX:/data/yourshare /NASmount
|
||||
|
||||
```
|
||||
and then your directory tree could look like this:
|
||||
```
|
||||
`/NASmount/home/user`
|
||||
```
|
||||
So, by simply changing the **/etc/fstab** entry, you could quickly be hooked up to someone else's cloud. This, as they say, is left as an exercise for the re
|
||||
@@ -1,288 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to test failed authentication attempts with test-driven development)
|
||||
[#]: via: (https://opensource.com/article/20/3/failed-authentication-attempts-tdd)
|
||||
[#]: author: (Alex Bunardzic https://opensource.com/users/alex-bunardzic)
|
||||
|
||||
How to test failed authentication attempts with test-driven development
|
||||
======
|
||||
Mountebank makes it easier to test the "less happy path" in your code.
|
||||
![Programming keyboard.][1]
|
||||
|
||||
Testing often begins with what we hope happens. In my [previous article][2], I demonstrated how to virtualize a service you depend on when processing the "happy path" scenario (that is, testing the outcome of a successful login attempt). But we all know that software fails in spectacular and unexpected ways. Now's the time to take a closer look into how to process the "less happy paths": what happens when someone tries to log in with the wrong credentials?
|
||||
|
||||
In the first article linked above, I walked through building a user authentication module. (Now is a good time to review that code and get it up and running.) This module does not do all the heavy lifting; it mostly relies on another service to do those tougher tasks—enable user registration, store the user accounts, and authenticate the users. The module will only be sending HTTP POST requests to this additional service's endpoint; in this case, **/api/v1/users/login**.
|
||||
|
||||
What do you do if the service you're dependent on hasn't been built yet? This scenario creates a blockage. In the previous post, I explored how to remove that blockage by using service virtualization enabled by [mountebank][3], a powerful test environment.
|
||||
|
||||
This article walks through the steps required to enable the processing of user authentication in cases when a user repeatedly attempts to log in. The third-party authentication service allows only three attempts to log in, after which it ceases to service the HTTP request arriving from the offending domain.
|
||||
|
||||
### How to simulate repeat requests
|
||||
|
||||
Mountebank makes it very easy to simulate a service that listens on a network port, matches the method and the path defined in the request, then handles it by sending back an HTTP response. To follow along, be sure to get mountebank running as we [did in the previous article][2]. As I explained there, these values are declared as JSONs that are posted to **<http://localhost:2525/imposters>**, mountebank's endpoint for processing authentication requests.
|
||||
|
||||
But the challenge now is how to simulate the scenario when the HTTP request keeps hitting the same endpoint from the same domain. This is necessary to simulate a user who submits invalid credentials (username and password), is informed they are invalid, tries different credentials, and is repeatedly rejected (or foolishly attempts to log in with the same credentials that failed on previous attempts). Eventually (in this case, after a third failed attempt), the user is barred from additional tries.
|
||||
|
||||
Writing executable code to simulate such a scenario would have to model very elaborate processing. However, when using mountebank, this type of simulated processing is extremely simple to accomplish. It is done by creating a rolling buffer of responses, and mountebank responds in the order the buffer was created. Here is an example of one way to simulate repeat requests in mountebank:
|
||||
|
||||
|
||||
```
|
||||
{
|
||||
"port": 3001,
|
||||
"protocol": "http",
|
||||
"name": "authentication imposter",
|
||||
"stubs": [
|
||||
{
|
||||
"predicates": [
|
||||
{
|
||||
"equals": {
|
||||
"method": "post",
|
||||
"path": "/api/v1/users/login"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": [
|
||||
{
|
||||
"is": {
|
||||
"statusCode": 200,
|
||||
"body": "Successfully logged in."
|
||||
}
|
||||
},
|
||||
{
|
||||
"is": {
|
||||
"statusCode": 400,
|
||||
"body": "Incorrect login. You have 2 more attempts left."
|
||||
}
|
||||
},
|
||||
{
|
||||
"is": {
|
||||
"statusCode": 400,
|
||||
"body": "Incorrect login. You have 1 more attempt left."
|
||||
}
|
||||
},
|
||||
{
|
||||
"is": {
|
||||
"statusCode": 400,
|
||||
"body": "Incorrect login. You have no more attempts left."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The rolling buffer is simply an unlimited collection of JSON responses where each response is represented with two key-value pairs: **statusCode** and **body**. In this case, four responses are defined. The first response is the happy path (i.e., user successfully logged in), and the remaining three responses represent failed use cases (i.e., wrong credentials result in status code 400 and corresponding error messages).
|
||||
|
||||
### How to test repeat requests
|
||||
|
||||
Modify the tests as follows:
|
||||
|
||||
|
||||
```
|
||||
using System;
|
||||
using Xunit;
|
||||
using app;
|
||||
namespace tests
|
||||
{
|
||||
public class UnitTest1
|
||||
{
|
||||
Authenticate auth = [new][4] Authenticate();
|
||||
[Fact]
|
||||
public void SuccessfulLogin()
|
||||
{
|
||||
var given = "valid credentials";
|
||||
var expected = " Successfully logged in.";
|
||||
var actual= auth.Login(given);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
[Fact]
|
||||
public void FirstFailedLogin()
|
||||
{
|
||||
var given = "invalid credentials";
|
||||
var expected = "Incorrect login. You have 2 more attempts left.";
|
||||
var actual = auth.Login(given);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
[Fact]
|
||||
public void SecondFailedLogin()
|
||||
{
|
||||
var given = “invalid credentials";
|
||||
var expected = "Incorrect login. You have 1 more attempt left.";
|
||||
var actual = auth.Login(given);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
[Fact]
|
||||
public void ThirdFailedLogin()
|
||||
{
|
||||
var given = " invalid credentials";
|
||||
var expected = "Incorrect login. You have no more attempts left.";
|
||||
var actual = auth.Login(given);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, run the tests to confirm that your code still works:
|
||||
|
||||
![Failed test][5]
|
||||
|
||||
Whoa! The tests now all fail. Why?
|
||||
|
||||
If you take a closer look, you'll see a revealing pattern:
|
||||
|
||||
![Reason for failed test][6]
|
||||
|
||||
Notice that ThirdFailedLogin is executed first, followed by the SuccessfulLogin, followed by FirstFailedLogin, followed by SecondFailedLogin. What's going on here? Why is the third test running before the first test?
|
||||
|
||||
The testing framework ([xUnit][7]) is executing all tests in parallel, and the sequence of execution is unpredictable. You need tests to run in order, which means you cannot test these scenarios using the vanilla xUnit toolkit.
|
||||
|
||||
### How to run tests in the right sequence
|
||||
|
||||
To force your tests to run in a certain sequence that you define (instead of running in an unpredictable order), you need to extend the vanilla xUnit toolkit with the NuGet [Xunit.Extensions.Ordering][8] package. Install the package on the command line with:
|
||||
|
||||
|
||||
```
|
||||
`$ dotnet add package Xunit.Extensions.Ordering --version 1.4.5`
|
||||
```
|
||||
|
||||
or add it to your **tests.csproj** config file:
|
||||
|
||||
|
||||
```
|
||||
`<PackageReference Include="Xunit.Extensions.Ordering" Version="1.4.5" />`
|
||||
```
|
||||
|
||||
Once that's taken care of, make some modifications to your **./tests/UnitTests1.cs** file. Add these four lines at the beginning of your **UnitTests1.cs **file:
|
||||
|
||||
|
||||
```
|
||||
using Xunit.Extensions.Ordering;
|
||||
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
||||
[assembly: TestCaseOrderer("Xunit.Extensions.Ordering.TestCaseOrderer", "Xunit.Extensions.Ordering")]
|
||||
[assembly: TestCollectionOrderer("Xunit.Extensions.Ordering.CollectionOrderer", "Xunit.Extensions.Ordering")]
|
||||
```
|
||||
|
||||
Now you can specify the order you want your tests to run. Initially, simulate the happy path (i.e., the **SuccessfulLogin()**) by annotating the test with:
|
||||
|
||||
|
||||
```
|
||||
[Fact, Order(1)]
|
||||
public void SuccessfulLogin() {
|
||||
```
|
||||
|
||||
After you test a successful login, test the first failed login:
|
||||
|
||||
|
||||
```
|
||||
[Fact, Order(2)]
|
||||
public void FirstFailedLogin()
|
||||
```
|
||||
|
||||
And so on. You can add the order of the test runs by simply adding the **Order(x)** (where **x** denotes the order you want the test to run) annotation to your Fact.
|
||||
|
||||
This annotation guarantees that your tests will run in the exact order you want them to run, and now you can (finally!) completely test your integration scenario.
|
||||
|
||||
The final version of your test is:
|
||||
|
||||
|
||||
```
|
||||
using System;
|
||||
using Xunit;
|
||||
using app;
|
||||
using Xunit.Extensions.Ordering;
|
||||
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
||||
[assembly: TestCaseOrderer("Xunit.Extensions.Ordering.TestCaseOrderer", "Xunit.Extensions.Ordering")]
|
||||
[assembly: TestCollectionOrderer("Xunit.Extensions.Ordering.CollectionOrderer", "Xunit.Extensions.Ordering")]
|
||||
namespace tests
|
||||
{
|
||||
public class UnitTest1
|
||||
{
|
||||
Authenticate auth = [new][4] Authenticate();
|
||||
[Fact, Order(1)]
|
||||
public void SuccessfulLogin()
|
||||
{
|
||||
var given = "[elon_musk@tesla.com][9]";
|
||||
var expected = "Successfully logged in.";
|
||||
var actual= auth.Login(given);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
[Fact, Order(2)]
|
||||
public void FirstFailedLogin()
|
||||
{
|
||||
var given = "[mickey@tesla.com][10]";
|
||||
var expected = "Incorrect login. You have 2 more attempts left.";
|
||||
var actual = auth.Login(given);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
[Fact, Order(3)]
|
||||
public void SecondFailedLogin()
|
||||
{
|
||||
var given = "[mickey@tesla.com][10]";
|
||||
var expected = "Incorrect login. You have 1 more attempt left.";
|
||||
var actual = auth.Login(given);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
[Fact, Order(4)]
|
||||
public void ThirdFailedLogin()
|
||||
{
|
||||
var given = "[mickey@tesla.com][10]";
|
||||
var expected = "Incorrect login. You have no more attempts left.";
|
||||
var actual = auth.Login(given);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Run the test again—everything passes!
|
||||
|
||||
![Passing test][11]
|
||||
|
||||
### What are you testing exactly?
|
||||
|
||||
This article has focused on test-driven development (TDD), but let's review it from another methodology, Extreme Programming (XP). XP defines two types of tests:
|
||||
|
||||
1. Programmer tests
|
||||
2. Customer tests
|
||||
|
||||
|
||||
|
||||
So far, in this series of articles on TDD, I have focused on the first type of tests (i.e., programmer tests). In this and the previous article, I switched my lenses to examine the most efficient ways of doing customer tests.
|
||||
|
||||
The important point is that programmer (or producer) tests are focused on precision work. We often refer to these precision tests as "micro tests," while others may call them "unit tests." Customer tests, on the other hand, are more focused on a bigger picture; we sometimes refer to them as "approximation tests" or "end-to-end tests."
|
||||
|
||||
### Conclusion
|
||||
|
||||
This article demonstrated how to write a suite of approximation tests that integrate several discrete steps and ensure that the code can handle all edge cases, including simulating the customer experience when repeatedly attempting to log in and failing to obtain the necessary clearance. This combination of TDD and tools like xUnit and mountebank can lead to well-tested and thus more reliable application development.
|
||||
|
||||
In future articles, I'll look into other usages of mountebank for writing customer (or approximation) tests.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/3/failed-authentication-attempts-tdd
|
||||
|
||||
作者:[Alex Bunardzic][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/alex-bunardzic
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/programming_keyboard_coding.png?itok=E0Vvam7A (Programming keyboard.)
|
||||
[2]: https://opensource.com/article/20/3/service-virtualization-test-driven-development
|
||||
[3]: http://www.mbtest.org/
|
||||
[4]: http://www.google.com/search?q=new+msdn.microsoft.com
|
||||
[5]: https://opensource.com/sites/default/files/uploads/testfails_0.png (Failed test)
|
||||
[6]: https://opensource.com/sites/default/files/uploads/failurepattern.png (Reason for failed test)
|
||||
[7]: https://xunit.net/
|
||||
[8]: https://www.nuget.org/packages/Xunit.Extensions.Ordering/#
|
||||
[9]: mailto:elon_musk@tesla.com
|
||||
[10]: mailto:mickey@tesla.com
|
||||
[11]: https://opensource.com/sites/default/files/uploads/testpasses.png (Passing test)
|
||||
@@ -1,772 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Run a command on binary files with this script)
|
||||
[#]: via: (https://opensource.com/article/20/3/run-binaries-script)
|
||||
[#]: author: (Nick Clifton https://opensource.com/users/nickclifton)
|
||||
|
||||
Run a command on binary files with this script
|
||||
======
|
||||
Try this simple script to easily run a command on binary files
|
||||
regardless of their packaging.
|
||||
![Binary code on a computer screen][1]
|
||||
|
||||
Examining files from the command-line is generally an easy thing to do. You just run the command you want, followed by a list of files to be examined. Dealing with binary files, however, is more complicated. These files are often packaged up into archives, tarballs, or other packaging formats. The run-on-binaries script provides a convenient way to run a command on a collection of files, regardless of how they are packaged.
|
||||
|
||||
The invocation of the script is quite simple:
|
||||
|
||||
|
||||
```
|
||||
`run-on-binaries <file(s}>`
|
||||
```
|
||||
|
||||
So, for example:
|
||||
|
||||
|
||||
```
|
||||
`run-on-binaries /usr/bin/ls foo.rpm`
|
||||
```
|
||||
|
||||
will list all of the files inside the **foo.rpm** file, while:
|
||||
|
||||
|
||||
```
|
||||
`run-on-binaries /usr/bin/readelf -a libc.a`
|
||||
```
|
||||
|
||||
will run the **readelf** program, with the **-a** command-line option, on all of the object files inside the **libc.a library**.
|
||||
|
||||
If necessary, the script can be passed a file containing a list of other files to be processed, rather than specifying them on the command line—like this:
|
||||
|
||||
|
||||
```
|
||||
`run-on-binaries --files-from=foo.lst /usr/bin/ps2ascii`
|
||||
```
|
||||
|
||||
This will run the **ps2ascii** script on all of the files listed in **foo.lst**. (The files just need to be separated by white space. There can be multiple files on a single line if desired).
|
||||
|
||||
Also, a skip list can be provided to stop the script from processing specified files:
|
||||
|
||||
|
||||
```
|
||||
`run-on-binaries --skip-list=skip.lst /usr/bin/wc *`
|
||||
```
|
||||
|
||||
This will run the **wc** program on all of the files in the current directory, except for those specified in **skip.lst**.
|
||||
|
||||
The script does not recurse into directories, but this can be handled by combining it with the **find** command, like this:
|
||||
|
||||
|
||||
```
|
||||
`find . -type f -exec run-on-binaries @ ;`
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
|
||||
```
|
||||
`find . -type d -exec run-on-binaries @/* ;`
|
||||
```
|
||||
|
||||
The only difference between these two invocations is that the second one only runs the target program once per directory, but gives it a long command-line of all of the files in the directory.
|
||||
|
||||
Though convenient, the script is lacking in several areas. Right now, it does not examine the PATH environment variable to find the command that it is asked to run, so a full path must be provided. Also, the script ought to be able to handle recursion on its own, without needing help from the find command.
|
||||
|
||||
The run-on-binaries script is part of the annobin package, which is available on Fedora. The sources for annobin can also be obtained from the git repository at <git://sourceware.org/git/annobin.git>.
|
||||
|
||||
### The script
|
||||
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
# Script to run another script/program on the executables inside a given file.
|
||||
#
|
||||
# Created by Nick Clifton. <[nickc@redhat.com][2]>
|
||||
# Copyright (c) 2018 Red Hat.
|
||||
#
|
||||
# This is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published
|
||||
# by the Free Software Foundation; either version 3, or (at your
|
||||
# option) any later version.
|
||||
|
||||
# It is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# Usage:
|
||||
# run-on-binaries-in [options] program [options-for-the-program] file(s)
|
||||
#
|
||||
# This script does not handle directories. This is deliberate.
|
||||
# It is intended that if recursion is needed then it will be
|
||||
# invoked from find, like this:
|
||||
#
|
||||
# find . -name "*.rpm" -exec run-on-binaries-in <script-to-run> {} \;
|
||||
|
||||
version=1.0
|
||||
|
||||
help ()
|
||||
{
|
||||
# The following exec goop is so that we don't have to manually
|
||||
# redirect every message to stderr in this function.
|
||||
exec 4>&1 # save stdout fd to fd #4
|
||||
exec 1>&2 # redirect stdout to stderr
|
||||
|
||||
cat <<__EOM__
|
||||
|
||||
This is a shell script to run another script/program on one or more binary
|
||||
files. If the file(s) specified are archives of some kind (including rpms)
|
||||
then the script/program is run on the binary executables inside the archive.
|
||||
|
||||
Usage: $prog {options} program {options-for-the-program} files(s)
|
||||
|
||||
{options} are:
|
||||
-h --help Display this information and then exit.
|
||||
-v --version Report the version number of this script.
|
||||
-V --verbose Report on progress.
|
||||
-q --quiet Do not include the script name in the output.
|
||||
-i --ignore Silently ignore files that are not executables or archives.
|
||||
-p=<TEXT> --prefix=<TEXT> Prefix normal output with this string.
|
||||
-t=<DIR> --tmpdir=<DIR> Temporary directory to use when opening archives.
|
||||
-f=<FILE> --files-from=<FILE> Process files listed in <FILE>.
|
||||
-s=<FILE> --skip-list=<FILE> Skip any file listed in <FILE>.
|
||||
-- Stop accumulating options.
|
||||
|
||||
Examples:
|
||||
|
||||
$prog hardened foo.rpm
|
||||
Runs the hardened script on the executable
|
||||
files inside foo.rpm.
|
||||
|
||||
$prog check-abi -v fred.tar.xz
|
||||
Runs the check-abi script on the decompressed
|
||||
contents of the fred.tar.xz archive, passing the
|
||||
-v option to check-abi as it does so.
|
||||
|
||||
$prog -V -f=list.txt readelf -a
|
||||
Runs the readelf program, with the -a option on
|
||||
every file listed in the list.txt. Describes
|
||||
what is being done as it works.
|
||||
|
||||
$prog -v -- -fred -a jim -b bert -- -c harry
|
||||
Runs the script "-fred" on the files jim, bert,
|
||||
"-c" and harry. Passes the options "-a" and
|
||||
"-b" to the script (even when run on jim).
|
||||
Reports the version of this script as well.
|
||||
|
||||
__EOM__
|
||||
exec 1>&4 # Copy stdout fd back from temporary save fd, #4
|
||||
}
|
||||
|
||||
main ()
|
||||
{
|
||||
init
|
||||
|
||||
parse_args ${1+"$@"}
|
||||
|
||||
if [ $failed -eq 0 ];
|
||||
then
|
||||
run_script_on_files
|
||||
fi
|
||||
|
||||
if [ $failed -ne 0 ];
|
||||
then
|
||||
exit 1
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
report ()
|
||||
{
|
||||
if [ $quiet -eq 0 ];
|
||||
then
|
||||
echo -n $prog": "
|
||||
fi
|
||||
|
||||
echo ${1+"$@"}
|
||||
}
|
||||
|
||||
ice ()
|
||||
{
|
||||
report "Internal error: " ${1+"$@"}
|
||||
exit 1
|
||||
}
|
||||
|
||||
fail ()
|
||||
{
|
||||
report "Failure:" ${1+"$@"}
|
||||
failed=1
|
||||
}
|
||||
|
||||
verbose ()
|
||||
{
|
||||
if [ $verbose -ne 0 ]
|
||||
then
|
||||
report ${1+"$@"}
|
||||
fi
|
||||
}
|
||||
|
||||
# Initialise global variables.
|
||||
init ()
|
||||
{
|
||||
files[0]="";
|
||||
# num_files is the number of files to be scanned.
|
||||
# files[0] is the script to run on the files.
|
||||
num_files=0;
|
||||
|
||||
script=""
|
||||
script_opts="";
|
||||
|
||||
prog_opts="-i"
|
||||
|
||||
tmpdir=/dev/shm
|
||||
prefix=""
|
||||
files_from=""
|
||||
skip_list=""
|
||||
|
||||
failed=0
|
||||
verbose=0
|
||||
ignore=0
|
||||
quiet=0
|
||||
}
|
||||
|
||||
# Parse our command line
|
||||
parse_args ()
|
||||
{
|
||||
abs_prog=$0;
|
||||
prog=`basename $abs_prog`;
|
||||
|
||||
# Locate any additional command line switches
|
||||
# Likewise accumulate non-switches to the files list.
|
||||
while [ $# -gt 0 ]
|
||||
do
|
||||
optname="`echo $1 | sed 's,=.*,,'`"
|
||||
optarg="`echo $1 | sed 's,^[^=]*=,,'`"
|
||||
case "$optname" in
|
||||
-v | --version)
|
||||
report "version: $version"
|
||||
;;
|
||||
-h | --help)
|
||||
help
|
||||
exit 0
|
||||
;;
|
||||
-q | --quiet)
|
||||
quiet=1;
|
||||
prog_opts="$prog_opts -q"
|
||||
;;
|
||||
-V | --verbose)
|
||||
if [ $verbose -eq 1 ];
|
||||
then
|
||||
# This has the effect of cancelling out the prog_opts="-i"
|
||||
# in the init function, so that recursive invocations of this
|
||||
# script will complain about unrecognised file types.
|
||||
if [ $quiet -eq 0 ];
|
||||
then
|
||||
prog_opts="-V -V"
|
||||
else
|
||||
prog_opts="-V -V -q"
|
||||
fi
|
||||
else
|
||||
verbose=1;
|
||||
prog_opts="$prog_opts -V"
|
||||
fi
|
||||
;;
|
||||
-i | --ignore)
|
||||
ignore=1
|
||||
;;
|
||||
-t | --tmpdir)
|
||||
if test "x$optarg" = "x$optname" ;
|
||||
then
|
||||
shift
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
fail "$optname needs a directory name"
|
||||
else
|
||||
tmpdir=$1
|
||||
fi
|
||||
else
|
||||
tmpdir="$optarg"
|
||||
fi
|
||||
;;
|
||||
-p | --prefix)
|
||||
if test "x$optarg" = "x$optname" ;
|
||||
then
|
||||
shift
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
fail "$optname needs a string argument"
|
||||
else
|
||||
prefix=$1
|
||||
fi
|
||||
else
|
||||
prefix="$optarg"
|
||||
fi
|
||||
;;
|
||||
-f | --files_from)
|
||||
if test "x$optarg" = "x$optname" ;
|
||||
then
|
||||
shift
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
fail "$optname needs a file name"
|
||||
else
|
||||
files_from=$1
|
||||
fi
|
||||
else
|
||||
files_from="$optarg"
|
||||
fi
|
||||
;;
|
||||
|
||||
-s | --skip-list)
|
||||
if test "x$optarg" = "x$optname" ;
|
||||
then
|
||||
shift
|
||||
if [ $# -eq 0 ]
|
||||
then
|
||||
fail "$optname needs a file name"
|
||||
else
|
||||
skip_list=$1
|
||||
fi
|
||||
else
|
||||
skip_list="$optarg"
|
||||
fi
|
||||
;;
|
||||
|
||||
--)
|
||||
shift
|
||||
break;
|
||||
;;
|
||||
--*)
|
||||
fail "unrecognised option: $1"
|
||||
help
|
||||
;;
|
||||
*)
|
||||
script="$1";
|
||||
if ! [ -a "$script" ]
|
||||
then
|
||||
fail "$script: program/script not found"
|
||||
elif ! [ -x "$script" ]
|
||||
then
|
||||
fail "$script: program/script not executable"
|
||||
fi
|
||||
# After we have seen the first non-option we stop
|
||||
# accumulating options for this script and instead
|
||||
# start accumulating options for the script to be
|
||||
# run.
|
||||
shift
|
||||
break;
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Read in the contents of the --file-from list, if specified.
|
||||
if test "x$files_from" != "x" ;
|
||||
then
|
||||
if ! [ -a "$files_from" ]
|
||||
then
|
||||
fail "$files_from: file not found"
|
||||
elif ! [ -r "$files_from" ]
|
||||
then
|
||||
fail "$files_from: file not readable"
|
||||
else
|
||||
eval 'files=($(cat $files_from))'
|
||||
num_files=${#files[*]}
|
||||
fi
|
||||
fi
|
||||
skip_files[foo]=bar
|
||||
|
||||
# Check that the skip list exists, if specified.
|
||||
if test "x$skip_list" != "x" ;
|
||||
then
|
||||
if ! [ -a "$skip_list" ]
|
||||
then
|
||||
fail "$skip_list: file not found"
|
||||
elif ! [ -r "$skip_list" ]
|
||||
then
|
||||
fail "$files_from: file not readable"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Accumulate any remaining arguments separating out the arguments
|
||||
# for the script from the names of the files to scan.
|
||||
while [ $# -gt 0 ]
|
||||
do
|
||||
optname="`echo $1 | sed 's,=.*,,'`"
|
||||
optarg="`echo $1 | sed 's,^[^=]*=,,'`"
|
||||
case "$optname" in
|
||||
--)
|
||||
shift
|
||||
break;
|
||||
;;
|
||||
-*)
|
||||
script_opts="$script_opts $1"
|
||||
;;
|
||||
*)
|
||||
files[$num_files]="$1";
|
||||
let "num_files++"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Accumulate any remaining arguments without processing them.
|
||||
while [ $# -gt 0 ]
|
||||
do
|
||||
files[$num_files]="$1";
|
||||
let "num_files++";
|
||||
shift
|
||||
done
|
||||
|
||||
if [ $num_files -gt 0 ];
|
||||
then
|
||||
# Remember that we are counting from zero not one.
|
||||
let "num_files--"
|
||||
else
|
||||
fail "Must specify a program/script and at least one file to scan."
|
||||
fi
|
||||
}
|
||||
|
||||
run_script_on_files ()
|
||||
{
|
||||
local i
|
||||
|
||||
i=0;
|
||||
while [ $i -le $num_files ]
|
||||
do
|
||||
run_on_file i
|
||||
let "i++"
|
||||
done
|
||||
}
|
||||
|
||||
# syntax: run <command> [<args>]
|
||||
# If being verbose report the command being run, and
|
||||
# the directory in which it is run.
|
||||
run ()
|
||||
{
|
||||
local where
|
||||
|
||||
if test "x$1" = "x" ;
|
||||
then
|
||||
fail "run() called without an argument."
|
||||
fi
|
||||
|
||||
verbose " Running: ${1+$@}"
|
||||
|
||||
${1+$@}
|
||||
}
|
||||
|
||||
decompress ()
|
||||
{
|
||||
local abs_file decompressor decomp_args orig_file base_file
|
||||
|
||||
# Paranoia checks - the user should never encounter these.
|
||||
if test "x$4" = "x" ;
|
||||
then
|
||||
ice "decompress called with too few arguments"
|
||||
fi
|
||||
if test "x$5" != "x" ;
|
||||
then
|
||||
ice "decompress called with too many arguments"
|
||||
fi
|
||||
|
||||
abs_file=$1
|
||||
decompressor=$2
|
||||
decomp_args=$3
|
||||
orig_file=$4
|
||||
|
||||
base_file=`basename $abs_file`
|
||||
|
||||
run cp $abs_file $base_file
|
||||
run $decompressor $decomp_args $base_file
|
||||
if [ $? != 0 ];
|
||||
then
|
||||
fail "$orig_file: Unable to decompress"
|
||||
fi
|
||||
|
||||
rm -f $base_file
|
||||
}
|
||||
|
||||
run_on_file ()
|
||||
{
|
||||
local file
|
||||
|
||||
# Paranoia checks - the user should never encounter these.
|
||||
if test "x$1" = "x" ;
|
||||
then
|
||||
ice "scan_file called without an argument"
|
||||
fi
|
||||
if test "x$2" != "x" ;
|
||||
then
|
||||
ice "scan_file called with too many arguments"
|
||||
fi
|
||||
|
||||
# Use quotes when accessing files in order to preserve
|
||||
# any spaces that might be in the directory name.
|
||||
file="${files[$1]}";
|
||||
|
||||
# Catch names that start with a dash - they might confuse readelf
|
||||
if test "x${file:0:1}" = "x-" ;
|
||||
then
|
||||
file="./$file"
|
||||
fi
|
||||
|
||||
# See if we should skip this file.
|
||||
if test "x$skip_list" != "x" ;
|
||||
then
|
||||
# This regexp looks for $file being the first text on a line, either
|
||||
# on its own, or with additional text separated from it by at least
|
||||
# one space character. So searching for "fred" in the following gives:
|
||||
# fr <\- no match
|
||||
# fred <\- match
|
||||
# fredjim <\- no match
|
||||
# fred bert <\- match
|
||||
regexp="^$file[^[:graph:]]*"
|
||||
grep --silent --regexp="$regexp" $skip_list
|
||||
if [ $? = 0 ];
|
||||
then
|
||||
verbose "$file: skipping"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check the file.
|
||||
if ! [ -a "$file" ]
|
||||
then
|
||||
fail "$file: file not found"
|
||||
return
|
||||
elif ! [ -r "$file" ]
|
||||
then
|
||||
if [ $ignore -eq 0 ];
|
||||
then
|
||||
fail "$file: not readable"
|
||||
fi
|
||||
return
|
||||
elif [ -d "$file" ]
|
||||
then
|
||||
if [ $ignore -eq 0 ];
|
||||
then
|
||||
if [ $num_files -gt 1 ];
|
||||
then
|
||||
verbose "$file: skipping - it is a directory"
|
||||
else
|
||||
report "$file: skipping - it is a directory"
|
||||
fi
|
||||
fi
|
||||
return
|
||||
elif ! [ -f "$file" ]
|
||||
then
|
||||
if [ $ignore -eq 0 ];
|
||||
then
|
||||
fail "$file: not an ordinary file"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
file_type=`file -b $file`
|
||||
case "$file_type" in
|
||||
*"ELF "*)
|
||||
verbose "$file: ELF format - running script/program"
|
||||
if test "x$prefix" != "x" ;
|
||||
then
|
||||
report "$prefix:"
|
||||
fi
|
||||
run $script $script_opts $file
|
||||
return
|
||||
;;
|
||||
"RPM "*)
|
||||
verbose "$file: RPM format."
|
||||
;;
|
||||
*" cpio "*)
|
||||
verbose "$file: CPIO format."
|
||||
;;
|
||||
*"tar "*)
|
||||
verbose "$file: TAR archive."
|
||||
;;
|
||||
*"Zip archive"*)
|
||||
verbose "$file: ZIP archive."
|
||||
;;
|
||||
*"ar archive"*)
|
||||
verbose "$file: AR archive."
|
||||
;;
|
||||
*"bzip2 compressed data"*)
|
||||
verbose "$file: contains bzip2 compressed data"
|
||||
;;
|
||||
*"gzip compressed data"*)
|
||||
verbose "$file: contains gzip compressed data"
|
||||
;;
|
||||
*"lzip compressed data"*)
|
||||
verbose "$file: contains lzip compressed data"
|
||||
;;
|
||||
*"XZ compressed data"*)
|
||||
verbose "$file: contains xz compressed data"
|
||||
;;
|
||||
*"shell script"* | *"ASCII text"*)
|
||||
if [ $ignore -eq 0 ];
|
||||
then
|
||||
fail "$file: test/scripts cannot be scanned."
|
||||
fi
|
||||
return
|
||||
;;
|
||||
*"symbolic link"*)
|
||||
if [ $ignore -eq 0 ];
|
||||
then
|
||||
# FIXME: We ought to be able to follow symbolic links
|
||||
fail "$file: symbolic links are not followed."
|
||||
fi
|
||||
return
|
||||
;;
|
||||
*)
|
||||
if [ $ignore -eq 0 ];
|
||||
then
|
||||
fail "$file: Unsupported file type: $file_type"
|
||||
fi
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
# We now know that we will need a temporary directory
|
||||
# so create one, and create paths to the file and scripts.
|
||||
if test "x${file:0:1}" = "x/" ;
|
||||
then
|
||||
abs_file=$file
|
||||
else
|
||||
abs_file="$PWD/$file"
|
||||
fi
|
||||
|
||||
if test "x${abs_prog:0:1}" != "x/" ;
|
||||
then
|
||||
abs_prog="$PWD/$abs_prog"
|
||||
fi
|
||||
|
||||
if test "x${script:0:1}" = "x/" ;
|
||||
then
|
||||
abs_script=$script
|
||||
else
|
||||
abs_script="$PWD/$script"
|
||||
fi
|
||||
|
||||
tmp_root=$tmpdir/delme.run.on.binary
|
||||
run mkdir -p "$tmp_root/$file"
|
||||
|
||||
verbose " Changing to directory: $tmp_root/$file"
|
||||
pushd "$tmp_root/$file" > /dev/null
|
||||
if [ $? != 0 ];
|
||||
then
|
||||
fail "Unable to change to temporary directory: $tmp_root/$file"
|
||||
return
|
||||
fi
|
||||
|
||||
# Run the file type switch again, although this time we do not need to
|
||||
# check for unrecognised types. (But we do, just in case...)
|
||||
# Note since are transforming the file we re-invoke the run-on-binaries
|
||||
# script on the decoded contents. This allows for archives that contain
|
||||
# other archives, and so on. We normally pass the -i option to the
|
||||
# invoked script so that it will not complain about unrecognised files in
|
||||
# the decoded archive, although we do not do this when running in very
|
||||
# verbose mode. We also pass an extended -t option to ensure that any
|
||||
# sub-archives are extracted into a unique directory tree.
|
||||
|
||||
case "$file_type" in
|
||||
"RPM "*)
|
||||
# The output redirect confuses the run function...
|
||||
verbose " Running: rpm2cpio $abs_file > delme.cpio"
|
||||
rpm2cpio $abs_file > delme.cpio
|
||||
if [ $? != 0 ];
|
||||
then
|
||||
fail "$file: Unable to extract from rpm archive"
|
||||
else
|
||||
# Save time - run cpio now.
|
||||
run cpio --quiet --extract --make-directories --file delme.cpio
|
||||
if [ $? != 0 ];
|
||||
then
|
||||
fail "$file: Unable to extract files from cpio archive"
|
||||
fi
|
||||
run rm -f delme.cpio
|
||||
fi
|
||||
;;
|
||||
|
||||
*" cpio "*)
|
||||
run cpio --quiet --extract --make-directories --file=$abs_file
|
||||
if [ $? != 0 ];
|
||||
then
|
||||
fail "$file: Unable to extract files from cpio archive"
|
||||
fi
|
||||
;;
|
||||
|
||||
*"tar "*)
|
||||
run tar --extract --file=$abs_file
|
||||
if [ $? != 0 ];
|
||||
then
|
||||
fail "$file: Unable to extract files from tarball"
|
||||
fi
|
||||
;;
|
||||
|
||||
*"ar archive"*)
|
||||
run ar x $abs_file
|
||||
if [ $? != 0 ];
|
||||
then
|
||||
fail "$file: Unable to extract files from ar archive"
|
||||
fi
|
||||
;;
|
||||
|
||||
*"Zip archive"*)
|
||||
decompress $abs_file unzip "-q" $file
|
||||
;;
|
||||
*"bzip2 compressed data"*)
|
||||
decompress $abs_file bzip2 "--quiet --decompress" $file
|
||||
;;
|
||||
*"gzip compressed data"*)
|
||||
decompress $abs_file gzip "--quiet --decompress" $file
|
||||
;;
|
||||
*"lzip compressed data"*)
|
||||
decompress $abs_file lzip "--quiet --decompress" $file
|
||||
;;
|
||||
*"XZ compressed data"*)
|
||||
decompress $abs_file xz "--quiet --decompress" $file
|
||||
;;
|
||||
*)
|
||||
ice "unhandled file type: $file_type"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ $failed -eq 0 ];
|
||||
then
|
||||
# Now scan the file(s) created in the previous step.
|
||||
run find . -type f -execdir $abs_prog $prog_opts -t=$tmp_root/$file -p=$file $abs_script $script_opts {} +
|
||||
fi
|
||||
|
||||
verbose " Deleting temporary directory: $tmp_root"
|
||||
rm -fr $tmp_root
|
||||
|
||||
verbose " Return to previous directory"
|
||||
popd > /dev/null
|
||||
}
|
||||
|
||||
# Invoke main
|
||||
main ${1+"$@"}
|
||||
```
|
||||
|
||||
|
||||
|
||||
Git has extensions for handling binary blobs such as multimedia files, so today we will learn how...
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/3/run-binaries-script
|
||||
|
||||
作者:[Nick Clifton][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[rakino](https://github.com/rakino)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/nickclifton
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/binary_code_computer_screen.png?itok=7IzHK1nn (Binary code on a computer screen)
|
||||
[2]: mailto:nickc@redhat.com
|
||||
@@ -1,234 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to detect outdated Kubernetes APIs)
|
||||
[#]: via: (https://opensource.com/article/20/3/deprek8)
|
||||
[#]: author: (Tyler Auerbeck https://opensource.com/users/tylerauerbeck)
|
||||
|
||||
How to detect outdated Kubernetes APIs
|
||||
======
|
||||
Deprek8 and Conftest alert you about deprecated APIs that threaten to
|
||||
slip into your codebase.
|
||||
![Ship captain sailing the Kubernetes seas][1]
|
||||
|
||||
Recently, deprecated APIs have been wreaking havoc on everyone's [Kubernetes][2] manifests. Why is this happening?!? It's because the objects that we've come to know and love are moving on to their new homes. And it's not like this happened overnight. Deprecation warnings have been in place for quite a few releases now. We've all just been lazy and thought the day would never come. Well, _it's here_!
|
||||
|
||||
So, maybe it caught up to us this time. But we'll be prepared next time, right?!? Yeah, that's what we said last time. But what if we could put something in place that makes sure that this doesn't happen?
|
||||
|
||||
### What is Deprek8?
|
||||
|
||||
[Deprek8][3] is a set of [Open Policy Agent][4] (OPA) policies that allow you to check your repository for deprecated API versions. These policies offer a way to provide warnings and errors when something is in the process of being or has already been deprecated. But **Deprek8** is just a set of policies that define what to watch for. How do you actually actively use these policies in order to monitor for deprecations?
|
||||
|
||||
There are a number of ways and tools that can do this; one way is to use the OPA Deprek8 policy.
|
||||
|
||||
### What is the OPA Deprek8 policy?
|
||||
|
||||
OPA is "an open source, general-purpose policy engine that enables unified, context-aware policy enforcement." In other words, OPA provides a means of establishing and enforcing a set of policies based upon a policy file. The policies are defined in a file (or set of files) using the [Rego query language][5]. This use case won't necessarily rely on the OPA application, but more specifically, it uses this query language to do the heavy lifting. By using Rego, you can check whether various manifests match certain criteria and then either warn or error them out based on your definition. For example, in Kubernetes 1.16, the Deployment object can no longer be served from the **extensions/v1beta1 apiVersion**. So in your .rego file, you could have something like:
|
||||
|
||||
|
||||
```
|
||||
_deny = msg {
|
||||
resources := ["Deployment"]
|
||||
input.apiVersion == "extensions/v1beta1"
|
||||
input.kind == resources[_]
|
||||
msg := sprintf("%s/%s: API extensions/v1beta1 for %s is no longer served by default, use apps/v1 instead.", [input.kind, input.metadata.name, input.kind])
|
||||
}
|
||||
```
|
||||
|
||||
This would alert that you have a deprecated manifest and print a message like:
|
||||
|
||||
> Deployment/myDeployment: API extensions/v1beta1 for Deployment is no longer served by default, use apps/v1 instead.
|
||||
|
||||
That's great! This is exactly what you need in order to avoid having old manifests lying around. But these are just the policies; you need something that will check these policies and put them into action.
|
||||
|
||||
### Conftest
|
||||
|
||||
This is where [Conftest][6] comes in. Conftest is a utility that allows you to put Rego policies into action against any number of configuration files. According to the repo, Conftest currently supports:
|
||||
|
||||
|
||||
```
|
||||
- YAML
|
||||
- JSON
|
||||
- INI
|
||||
- TOML
|
||||
- HOCON
|
||||
- HCL
|
||||
- CUE
|
||||
- Dockerfile
|
||||
- HCL2 (Experimental)
|
||||
- EDN
|
||||
- VCL
|
||||
- XML
|
||||
```
|
||||
|
||||
It has some fairly strict defaults (i.e., expecting policy files to be in certain locations), but they can be overridden with the appropriate flags if you have a layout that you prefer. If you want to know more about those specifics, please consult the [documentation][7] in the repository.
|
||||
|
||||
For example, you can run any policy file on Conftest with a command like:
|
||||
|
||||
|
||||
```
|
||||
`helm template --set podSecurityPolicy.enabled=true --set server.ingress.enabled=true . | conftest -p mypolicy.rego -`
|
||||
```
|
||||
|
||||
This would generate the appropriate output from a Helm template and pipe it directly to the Conftest utility. Conftest inspects that output against any policies defined in the **mypolicy.rego** file and then gives any appropriate warnings or errors for objects that match against those policies. You can, of course, swap out any templating tooling of your choice, or you can feed specific files directly to the Conftest tool.
|
||||
|
||||
So now you have the tools to set your policies and enforce them against your configuration files. But how do you tie these two things together? Better yet: How do you automate this process to continuously monitor the codebase to make sure you never fall behind the deprecation line again?
|
||||
|
||||
### Using Git to run checks
|
||||
|
||||
There are many methods and tools to run checks against code. By adding similar steps to your continuous integration (CI) tooling (e.g., Jenkins, Tekton, etc.), you can accomplish the same goal. In this very basic use case, I used [GitHub Actions][8], a new feature of GitHub repositories.
|
||||
|
||||
GitHub Actions allows you to automate your entire workflow, so you don't have to sit in front of your keyboard and hack all of this together. With Actions, you can string together any number of steps into a workflow (or multiple workflows) by either rolling your own Actions if you're doing something custom or, in most cases, using something that already exists in the [Marketplace][9]. Luckily, others have provided Actions to do the things you need to do for this example, so you can lean on the community's expertise to pull your workflow together.
|
||||
|
||||
As described in the steps above, the workflow looks something like:
|
||||
|
||||
1. Retrieve the Deprek8 policy you need and store it somewhere for later use.
|
||||
2. Run Conftest against the appropriate files/charts with the policy file you grabbed in step 1.
|
||||
|
||||
|
||||
|
||||
What does this boil down to? Well, all you really need to do is to use curl to pull your policy file and then run it through Conftest after pointing to your code, using the [curl][10] and [Conftest][11] Actions. Since these Actions already exist, you don't need to write any custom code! And as I'm sure you can tell by the names, they allow you to run the associated commands without having to do any custom work to pre-process anything or pull down any binaries.
|
||||
|
||||
Now that you have the Actions you need to use, how do you pull them together? This is where your workflow comes into play. While Actions are the pieces of code that get things done, they're useless without a way to string them together so that they can be triggered by some event. A GitHub Action workflow will look something like this:
|
||||
|
||||
|
||||
```
|
||||
name: Some Awesome Workflow Name
|
||||
on: An Event That Triggers Our Workflow
|
||||
jobs:
|
||||
awesome-job-name:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: awesome-step-name
|
||||
uses: someorg/someaction@version
|
||||
with:
|
||||
args: some args that I might pass to someaction
|
||||
```
|
||||
|
||||
Now you have a workflow that has multiple steps, can be triggered by a specific GitHub event, and can be passed a set of parameters (if that is applicable to that specific Action). This example is _extremely basic_. But luckily, the workflow you're trying to put together is equally simple. This shouldn't be taken as a comprehensive example of a GitHub Action, as there are many more complicated (and elegant) things you can do. If you're interested in learning more, take a look at the [GitHub Actions documentation][12].
|
||||
|
||||
Now that you have an idea of what a workflow looks like and know what Actions you're interested in using, take a run at plugging the two together. For this example, you want to make sure that whenever your code is updated, it's checked to make sure it's not using any deprecated APIs.
|
||||
|
||||
First, rig up your workflow with some names and the events that you want to trigger off of. Give your workflow and job a useful name that will help you identify it (and what it does).
|
||||
|
||||
|
||||
```
|
||||
name: API Deprecation Check
|
||||
on: pull_request, push
|
||||
jobs:
|
||||
deprecation-check:
|
||||
```
|
||||
|
||||
Next, you need to tell your workflow that you want to trigger these Actions based on any **pull_request** or **push** that happens to this repository because these are the two main events that get new code into a repository. You can do this by utilizing the **on** keyword.
|
||||
|
||||
|
||||
```
|
||||
name: API Deprecation Check
|
||||
on: pull_request, push
|
||||
jobs:
|
||||
deprecation-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
```
|
||||
|
||||
Then, add where you want these Actions to run and how the Action can get the code. You can tell the Action where to run by using the **runs-on** keyword. You have a few options here: Windows, Mac, or Ubuntu. In most cases, using Ubuntu is fine, as you'll frequently rely on Actions that run inside their own container (versus running on the base OS that you define here). It's also very important to understand that an Action does not check out code by default. When you need to do something that interacts with your code, make sure to use the Action **actions/checkout**. When this is included, your code will be available within your Action, and you can pass that through to the next step in your workflow.
|
||||
|
||||
|
||||
```
|
||||
name: API Deprecation Check
|
||||
on: pull_request, push
|
||||
name: API Deprecation Check
|
||||
jobs:
|
||||
deprecation-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: curl
|
||||
uses: wei/curl@master
|
||||
with:
|
||||
args: <https://raw.githubusercontent.com/naquada/deprek8/master/policy/deprek8.rego> > /github/home/deprek8.rego
|
||||
```
|
||||
|
||||
Now that your code is checked out, you can start preparing to do something with it. As mentioned, before you can check code for deprecations, you first need the file that contains the policies that you want to check for, so just retrieve the file using the **curl** Action. This is a fairly straightforward Action, in that it accepts whatever parameters you would normally pass into the curl command. If you were doing something more complicated, this is where you could pass in things like specific HTTP Actions, headers, etc. However, in this case, you're just trying to retrieve a file, so the only thing you need to pass to your Action is the URL you want to retrieve (in this case, the one that contains your raw policy file) and then tell it where you want to write that file. In this case, you're going to have it write to **/github/home**. Why? It's because this filesystem persists between steps and will allow you to use the policy file within this next step.
|
||||
|
||||
|
||||
```
|
||||
name: API Deprecation Check
|
||||
on: pull_request, push
|
||||
jobs:
|
||||
deprecation-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: curl
|
||||
uses: wei/curl@master
|
||||
with:
|
||||
args: <https://raw.githubusercontent.com/naquada/deprek8/master/policy/deprek8.rego> > /github/home/deprek8.rego
|
||||
- name: Check helm chart for deprecation
|
||||
uses: instrumenta/conftest-action/helm@master
|
||||
with:
|
||||
chart: nginx-test
|
||||
policy: /github/home/deprek8.rego
|
||||
```
|
||||
|
||||
Now that you have your policy file, it's just a matter of running it against the code via **conftest**. Similar to the **curl** Action, the **conftest** Action just expects a series of parameters to understand how it should run against the code. In the example above, it runs against a Helm chart, but it can run against a specific file (or set of files) by changing the **uses** value to **instrumenta/[conftest-action@master][13]**. Just point to the path where your chart sits in the repository and then provide the path to your policy file (specified in the previous step). Once you have all of this together, you have a complete workflow. But what does this look like (assuming there's some bad code in your Helm chart)? To find out, take a look at the [example repository][14].
|
||||
|
||||
In the Nginx Helm chart, you'll notice that one of the templates is a [statefulset][15]. You may also notice that the apiVersion the StatefulSet is using is **apps/v1beta1**. This API was deprecated in Kubernetes 1.16 and is now hosted in **apps/v1**. So when your GitHub Actions workflow runs, it should detect this issue and serve an error like:
|
||||
|
||||
|
||||
```
|
||||
FAIL - StatefulSetf/web: API apps/v1beta1 is no longer served by default, use apps/v1 instead.
|
||||
Error: plugin "conftest" exited with error
|
||||
##[error]Docker run failed with exit code 1
|
||||
```
|
||||
|
||||
The Action indicates there is something wrong and then fails the rest of the Action. You can see the [full workflow][16] if you are interested.
|
||||
|
||||
### Wrapping up
|
||||
|
||||
This workflow will save some future heartache by alerting you to any deprecated APIs that slip into your codebase. To be clear, this is an _alerting_ mechanism. This won't prevent you from merging bad code into your codebase. But, as long as you pay attention, you should be completely aware prior to (or just after) merging problematic code.
|
||||
|
||||
Where do you go from here? Well, there are a few things to keep in mind. Currently, Deprek8 is up to date as of Kubernetes 1.16. If you're interested in more recent versions, I'm sure Deprek8 would be happy to accept your [pull request][3].
|
||||
|
||||
The other shortcoming of this method is that the **conftest** and GitHub Actions are a bit limited in that they only allow you to point at specific files or a single chart at a time. What if you want to point at multiple directories of manifests or have multiple charts inside your repository? Currently, the only way to get around that is to either list out every single file you're interested in (in the case of having multiple charts) or have multiple steps inside your workflow. Other scenarios could become problematic, like other templating engines that require some custom logic to pair the parameters and template files together. But a simple workaround for that could be to have a step in your workflow that pulls down Conftest along with a tiny inline script to loop through some of this. I'm sure there are more elegant solutions (and if you come up with one, I'm sure these projects would be more than happy to take a look at your PR).
|
||||
|
||||
Regardless, you now have a mechanism that should allow you to sleep a bit easier when checking in your code! And hopefully, this method will help you build even more robust workflows to protect your code.
|
||||
|
||||
* * *
|
||||
|
||||
_This was originally published in [Tyler Auerbeck's GitHub repository][17] and is reposted, with edits, with permission._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/3/deprek8
|
||||
|
||||
作者:[Tyler Auerbeck][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/tylerauerbeck
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/ship_captain_devops_kubernetes_steer.png?itok=LAHfIpek (Ship captain sailing the Kubernetes seas)
|
||||
[2]: https://opensource.com/resources/what-is-kubernetes
|
||||
[3]: https://github.com/naquada/deprek8
|
||||
[4]: https://github.com/open-policy-agent/opa
|
||||
[5]: https://blog.openpolicyagent.org/opas-full-stack-policy-language-caeaadb1e077
|
||||
[6]: https://github.com/instrumenta/conftest
|
||||
[7]: https://github.com/instrumenta/conftest/tree/master/docs
|
||||
[8]: https://github.com/features/actions
|
||||
[9]: https://github.com/marketplace?type=actions
|
||||
[10]: https://github.com/marketplace/actions/github-action-for-curl
|
||||
[11]: https://github.com/instrumenta/conftest-action
|
||||
[12]: https://help.github.com/en/actions
|
||||
[13]: mailto:conftest-action@master
|
||||
[14]: https://github.com/tylerauerbeck/deprek8-example
|
||||
[15]: https://raw.githubusercontent.com/tylerauerbeck/deprek8-example/master/nginx-test/templates/statefulset.yaml
|
||||
[16]: https://github.com/tylerauerbeck/deprek8-example/runs/426774566?check_suite_focus=true
|
||||
[17]: https://github.com/tylerauerbeck/writing/blob/master/opa/deprek8.md
|
||||
@@ -1,79 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (KubeCF Is What DevOps Wanted: Marrying Cloud Foundry with Kubernetes )
|
||||
[#]: via: (https://www.linux.com/articles/kubecf-is-what-devops-wanted-marrying-cloud-foundry-with-kubernetes/)
|
||||
[#]: author: (Swapnil Bhartiya https://www.linux.com/author/swapnil/)
|
||||
|
||||
KubeCF Is What DevOps Wanted: Marrying Cloud Foundry with Kubernetes
|
||||
======
|
||||
|
||||
[![][1]][2]
|
||||
|
||||
There are times when solutions that seem to compete against each other turn out to be complementary. [This is exactly what happened with Cloud Foundry and Kubernetes. ][3]
|
||||
|
||||
“Enterprises have moved on from the debate around Cloud Foundry Application Runtime or PaaS experience versus Kubernetes-based experience, and have opted to adopt both. The PaaS experience that Cloud Foundry offers is about optimizing developer time by allowing them to focus on business problems. Let them focus on the app they’re trying to build, not on the plumbing underneath it,” says Chip Childers, CTO at Cloud Foundry Foundation.
|
||||
|
||||
“There are plenty of use cases that don’t fit into a PaaS-style architecture. If you look at the breadth of architectures that a typical enterprise deals with, there are tons of applications that you need to just wrap the thing in a container and operate it that way,” he says.
|
||||
|
||||
**Integrating Cloud Foundry and Kubernetes **
|
||||
|
||||
To incorporate Kubernetes into the Cloud Foundry architecture so Cloud Foundry users can use Kubernetes as an alternative to Diego/Garden to orchestrate application container instances, Cloud Foundry Foundation kickstarted an initiative called [Project Eirini][4].
|
||||
|
||||
Similarly, [Project Quarks][5] is another incubating effort within the Cloud Foundry Foundation that is focused on packaging the Cloud Foundry Application Runtime as a set of containers instead of virtual machines, enabling easier deployment to Kubernetes.
|
||||
|
||||
“Project Quarks took some code from SUSE called Fizzle. It would take the type of release artifact that our project teams were generally releasing for their component of the system, and work it into a usable Docker image. Then they would use Helm and some scripts to deploy that into Kubernetes,” said Childers.
|
||||
|
||||
There was, however, one crucial piece of the jigsaw missing.
|
||||
|
||||
“There was this code that SUSE had been working on. It was the basis of the SUSE product called SCF (SUSE Cloud Foundry). SCF became [KubeCF][6]. It creates a Kubernetes native distribution of Cloud Foundry,” reveals Childers.
|
||||
|
||||
KubeCF recently hit its 1.0 release. So, where does KubeCF go from here?
|
||||
|
||||
“Presenty, we can look at it as the easiest path to a Kubernetes-native Cloud Foundry for pure open search users. There are some other efforts that are happening in parallel that are taking a look at each component of the Cloud Foundry architecture. Project architects are working towards allowing people to take KubeCF, deploy the whole system to Kubernetes, and take advantage of the simplicity that gets enabled as code gets modified,” says Childers.
|
||||
|
||||
**Developers First: Ensuring Seamless UX**
|
||||
|
||||
One of the strengths of the Cloud Foundry community is that it has always offered a distribution. There is still a distribution called CF deployment, which is based on a VM-centric architecture that uses the Cloud Foundry BOSH platform to orchestrate infrastructures and service environments (or virtualized environments like V-sphere) to deploy VMs and then run the system on top of it.
|
||||
|
||||
However, distribution also entails members of the ecosystem packaging it or dictating with their own offerings. In such a scenario, how can developers expect consistency across the distros?
|
||||
|
||||
“The commonality between both upstream releases — the CF deployment and KubeCF — lies in the components that they sew together to create the Cloud Foundry platform. The Cloud Foundry Platform certification continues to be based on the idea that a certified distribution uses those components in an unmodified way, and integrates them to create that developer experience,” Childers explains.
|
||||
|
||||
“So, regardless of whether you’re deploying to Kubernetes or you’re deploying to virtual machines, regardless of the certified vendor that you use, or if you use upstream distributions, you should have that same developer experience. That’s what Platform Certification ensures,” he says.
|
||||
|
||||
Operational consistency is less a concern for the ecosystem and the community because many of these providers are offering it as a service. “Those that do it as subscription-based software delivery have a lot of tooling around operations that’s specific to them plus all of the other values that they bring together,” Childers avers.
|
||||
|
||||
**Evolution Unabated**
|
||||
|
||||
The KubeCF distribution has finally put the debate of ‘Do I use Kubernetes or do I use Cloud Foundry?’ to rest.
|
||||
|
||||
“The answer should’ve always been ‘You use both.’ The architecture fits one on top of the other very nicely, and overcomes the concerns of dual stacks,” says Childers.
|
||||
|
||||
With the ‘either-or’ debate over, Chip intends to further evolve this architecture, thereby offering an enormous amount of value to enterprises trying to deal with container-centric infrastructure management and developer productivity.
|
||||
|
||||
“We’ve completely re-converged as an ecosystem around embracing the Kubernetes-based infrastructure as being the most popular and rising approach. We’ve been evolving this architecture as a community for years now,” he says.
|
||||
|
||||
“There are huge engineering and commercial teams supporting Kubernetes. VMware has an enormous investment in Kubernetes, and it continues to increase that investment. With the acquisition of Pivotal, VMware also has a huge amount of investment in Cloud Foundry. It’s working aggressively on the mission of bringing the two together. We see the same traction with SAP, IBM, and SUSE, which presents a lot of opportunities for everybody,” adds Childers.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/articles/kubecf-is-what-devops-wanted-marrying-cloud-foundry-with-kubernetes/
|
||||
|
||||
作者:[Swapnil Bhartiya][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.linux.com/author/swapnil/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.linux.com/wp-content/uploads/2020/03/color-3580779_1920-1068x667.jpg (color-3580779_1920)
|
||||
[2]: https://www.linux.com/wp-content/uploads/2020/03/color-3580779_1920.jpg
|
||||
[3]: https://www.tfir.io/kubecf-a-kubernetes-native-distribution-of-cloud-foundry-chip-childers-cto-cloud-foundry-%e2%80%8bfoundation/
|
||||
[4]: https://www.cloudfoundry.org/project-eirini/
|
||||
[5]: https://www.cloudfoundry.org/project-quarks/
|
||||
[6]: https://github.com/cloudfoundry-incubator/kubecf
|
||||
@@ -1,82 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Open source fights against COVID-19, Google's new security tool written in Python, and more open source news)
|
||||
[#]: via: (https://opensource.com/article/20/3/news-march-28)
|
||||
[#]: author: (Scott Nesbitt https://opensource.com/users/scottnesbitt)
|
||||
|
||||
Open source fights against COVID-19, Google's new security tool written in Python, and more open source news
|
||||
======
|
||||
Catch up on the biggest open source headlines from the past two weeks.
|
||||
![][1]
|
||||
|
||||
In this edition of our open source news roundup, we take a look open source solutions for COVID-19, Google's new security tool, code cleanup software from Uber, and more!
|
||||
|
||||
### Using open source in the fight against COVID-19
|
||||
|
||||
When COVID-19 started its march around the world, open source [stepped up][2] to try to help stop it. That includes using open data to [create tracking dashboards and apps][3], designing ventilators, and developing protective gear.
|
||||
|
||||
Scientists at the University of Waterloo in Canada have teamed with artificial intelligence firm DarwinAI to create an open source tool "[to identify signs of Covid-19 in chest x-rays][4]." Called COVID-Net, it's neural network "that is particularly good at recognizing images." The dataset the researchers are using is [available on GitHub][5], which includes a link the software.
|
||||
|
||||
Additionally, many [open source hardware projects][6] are underway to expedite the search for a cure.
|
||||
|
||||
### Google releases tool to fight USB keystroke injection attacks
|
||||
|
||||
One of the sneakiest and potentially most malicious ways to hack a computer is a USB keystroke injection attack. Using a compromised USB device connected to a computer, a hacker can run commands without you even noticing. Google's making it easier for Linux users to fight back against these kinds of attacks by releasing [an open source detection tool][7].
|
||||
|
||||
Called USB Keystroke Injection Protection, the tool detects "if the keystrokes have been made without human involvement". It does that by measuring "the timing of keystrokes coming from connected USB devices." Sebastian Neuner of Google's Information Security Engineering Team said that while the USB Keystroke Injection Protection tool isn't the last word in defense against these kinds of attacks, but offers "another layer of protection and to defend a user sitting in front of their unlocked machine by them seeing the attack happening."
|
||||
|
||||
You can find the Python source code for the tool [on GitHub][8].
|
||||
|
||||
### Uber makes code deletion tool open source
|
||||
|
||||
As applications get bigger, they often contain code that's either no longer used or which is obsolete. That added code make software more difficult to maintain. To help solve the problem of quickly finding that redundant code, Uber recently [open sourced a tool called Pirhana][9].
|
||||
|
||||
Pirhana scans code for [feature flags][10], looking for ones that are no longer used. The software then deletes the unused flags from the code. At the moment, Pirhana works with software written in the Objective-C, Swift, and Java languages. Uber's developers hope the number of supported languages will increase "now that outside developers have an opportunity to contribute to the project."
|
||||
|
||||
You can grab [Pirhana's source code][11] from its repository on GitHub
|
||||
|
||||
#### In other news
|
||||
|
||||
* [Singapore government to open source contact-tracing protocol][12]
|
||||
* [European Commission to use open source messaging service Signal][13]
|
||||
* [Spanish software to computerize healthcare in Cameroon and India][14]
|
||||
* [ING Open-Sources Lion, Its White-Label Web Component Library][15]
|
||||
* [Open Source Goes Mainstream – How Sharing Is Shaping The Future Of Music][16]
|
||||
|
||||
|
||||
|
||||
Thanks, as always, to Opensource.com staff members and [Correspondents][17] for their help this week.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/3/news-march-28
|
||||
|
||||
作者:[Scott Nesbitt][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/scottnesbitt
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/weekly_news_roundup_tv.png?itok=tibLvjBd
|
||||
[2]: https://jaxenter.com/covid-19-open-source-170237.html
|
||||
[3]: https://opensource.com/article/20/3/open-source-software-covid19
|
||||
[4]: https://www.technologyreview.com/s/615399/coronavirus-neural-network-can-help-spot-covid-19-in-chest-x-ray-pneumonia/
|
||||
[5]: https://github.com/lindawangg/COVID-Net
|
||||
[6]: https://opensource.com/article/20/3/open-hardware-covid19
|
||||
[7]: https://www.zdnet.com/article/google-linux-systems-can-use-this-new-tool-against-usb-keystroke-injection-attacks/
|
||||
[8]: https://github.com/google/ukip
|
||||
[9]: https://siliconangle.com/2020/03/17/ubers-open-source-piranha-tool-hunts-redundant-application-code/
|
||||
[10]: https://en.wikipedia.org/wiki/Feature_toggle
|
||||
[11]: https://github.com/uber/piranha
|
||||
[12]: https://www.computerweekly.com/news/252480501/Singapore-government-to-open-source-contact-tracing-protocol
|
||||
[13]: https://joinup.ec.europa.eu/collection/open-source-observatory-osor/news/signal-messaging-service
|
||||
[14]: https://intallaght.ie/spanish-software-to-computerize-healthcare-in-cameroon-and-india/
|
||||
[15]: https://www.infoq.com/articles/ing-open-sources-lion-web-component/
|
||||
[16]: https://www.forbes.com/sites/andreazarczynski/2020/03/19/open-source-goes-mainstream--how-sharing-is-shaping-the-future-of-music/#9e1ca1290013
|
||||
[17]: https://opensource.com/correspondent-program
|
||||
@@ -1,154 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Nextcloud: The Swiss Army Knife of Remote Working Tools)
|
||||
[#]: via: (https://itsfoss.com/nextcloud/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
Nextcloud: The Swiss Army Knife of Remote Working Tools
|
||||
======
|
||||
|
||||
Remote working culture has been booming for past few years in coding, graphics and other IT related fields. But the recent [Coronavirus pandemic][1] has made it mandatory for the companies to work from home if it’s possible for them.
|
||||
|
||||
While there are tons of tools to help you and your organization in working from home, let me share one open source software that has the features of several such tools combined into one.
|
||||
|
||||
### Nextcloud Hub: A Suite of Essential Tools for Remote Collaboration
|
||||
|
||||
[Nextcloud][2] is an open source software that can be used to store files, photos and videos for personal usage like Dropbox. But it’s more than just a private [cloud service][3].
|
||||
|
||||
You can add more than one users in Nextcloud and turn it into a collaboration platform for editing files in real time, chat with users, manage calendars, assign and manage tasks and more.
|
||||
|
||||
This video gives a good overview of its main features:
|
||||
|
||||
[Subscribe to our YouTube channel for more Linux videos][4]
|
||||
|
||||
### Main Features of Nextcloud
|
||||
|
||||
Let me highlight the main features of Nextcloud:
|
||||
|
||||
#### Sync files and share
|
||||
|
||||
![Nextcloud Files][5]
|
||||
|
||||
You can create workspaces based on user groups and share files in those folders. Users can create private files and folders and share them with selected users internally or externally (if they are allowed to). You can lock files in read only mode as well.
|
||||
|
||||
It also has a very powerful search feature that lets you search files from their name or tags. You can comment on files to provide feedback.
|
||||
|
||||
Text files can be edited in real time thanks to its builtin markdown editor. You can use OnlyOffice or Collabora to allow editing of docs, spreadsheet and presentations in real time.
|
||||
|
||||
It also has version control for the files so that you can revert changes easily.
|
||||
|
||||
#### Text Chat, Audio Chat, Video Chat and Web Meetings
|
||||
|
||||
![Nextcloud Video Call][6]
|
||||
|
||||
With NextCloud Talk, you can interact with other users by text messaging, audio calls, video calls and group calls for web meetings. You can also take meeting minutes during the video calls and share your screen for presentations. There is also a mobile app to stay connected all the time.
|
||||
|
||||
You can also create Slack like channels (known as circles) to communicate between members concerned with a specific topic.
|
||||
|
||||
#### Calendar, Contacts & Mail
|
||||
|
||||
![Calendar Nextcloud][7]
|
||||
|
||||
You can manage all of your organization’s contact, divide them into groups based on departments.
|
||||
|
||||
With the calendar, you can see when someone is free or what meetings are taking place, like you do on Outlook.
|
||||
|
||||
You can also use the Mail feature and import the emails from other providers to use them inside Nextcloud interface.
|
||||
|
||||
#### Kanban project management with Deck
|
||||
|
||||
![][8]
|
||||
|
||||
Like Trello and Jira, you can create boards for various projects. You can create cards for each tasks, assign them to users and they can move it between the list based on the status of the task. It’s really up to you how you create boards to manage your projects in Kanban style.
|
||||
|
||||
#### Plenty of add-ons to get more out of Nextcloud
|
||||
|
||||
![Password Manager][9]
|
||||
|
||||
Nextcloud also has several add-ons (called apps). Some are developed by Nextcloud teams while some are from third-party developers. You may use them to extend the capability of Nextcloud.
|
||||
|
||||
For example, you can add a [Feedly style feed reader][10] and read news from various sources. Similarly, the [Paswords addon][11] lets you use Netxcloud as a password manager. You can even share common passwords with other Nextcloud users.
|
||||
|
||||
You can explore [all the apps on its website][12]. You’ll also notice the ratings of apps that will help you decide if you should use an app or not.
|
||||
|
||||
#### Many more features
|
||||
|
||||
Let me summarize all the features here:
|
||||
|
||||
* Open source software that lets you own your data on your own servers
|
||||
* Seamlessly edit office documents together with others
|
||||
* Communicate with other members of your organization and do audio and video calls and held web meetings
|
||||
* Calendar lets you book meetings, brings busy view for meetings and resource booking and more
|
||||
* Manage users locally or authenticate through LDAP / Active Directory, Kerberos and Shibboleth / SAML 2.0 and more
|
||||
* Secure data with powerful file access control, multi-layer encryption, machine-learning based authentication protection and advanced ransomware recovery capabilities
|
||||
* Access existing storage silos like FTP, Windows Network Drives, SharePoint, Object Storage and Samba shares seamlessly through Nextcloud.
|
||||
* Automation: Automatically turn documents in PDFs, send messages to chat rooms and more!
|
||||
* Built in ONLYOFFICE makes collaborative editing of Microsoft Office documents accessible to everyone
|
||||
* Users can install desktop and mobile apps or simply use it in web browser
|
||||
|
||||
|
||||
|
||||
### How to get Nextcloud
|
||||
|
||||
![][13]
|
||||
|
||||
NextCloud is free and open source software. You can download it and install it on your own server.
|
||||
|
||||
You can use cloud server providers like [Linode][14] or [DigitalOcean][15] that allow you to deploy a brand new Linux server within minutes. And then you can use Docker to install NextCloud. At It’s FOSS, we use [Linode][14] for our NextCloud instance.
|
||||
|
||||
If you don’t want to do that, you can [signup with one of the Nextcloud partners][16] that provide you with configured Nextcloud instance. Some providers also provide a few GB of free data to try it.
|
||||
|
||||
Nextcloud also has an [enterprise plan][17] where Nextcloud team itself handles everything for the users and provide premium support. You can check their pricing [here][18].
|
||||
|
||||
If you decide to use Nextcloud, you should refer to its documentation or community forum to explore all its features.
|
||||
|
||||
### Conclusion
|
||||
|
||||
At It’s FOSS, our entire team works remote. We have no centralized office anywhere and all of us work from our home. Initially we relied on non-open source tools like Slack, Google Drive etc but lately we are migrating to their open source alternatives.
|
||||
|
||||
Nextcloud is one of the first software we tried internally. It has features of Dropbox, Google Docs, [Slack][19], [Trello][20], Google Hangout all combined in one software.
|
||||
|
||||
NextCloud works for most part but we found it struggling with the video calls. I think that has to do with the fact that we have it installed on a server with 1 GB of RAM that also runs some other web services like [Ghost CMS][21]. We plan to move it to a server with better specs. We’ll see if that should address these issues.
|
||||
|
||||
Since the entire world is struggling with the Coronavirus pandemic, using a solution like Nextcloud could be helpful for you and your organization in working from home.
|
||||
|
||||
How are you coping during the Coronavirus lockdown? Like [Linus Torvalds’ advice on remote working][22], do you also have some suggestion to share with the rest of us? Please feel free to use the comment section.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/nextcloud/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/abhishek/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://en.wikipedia.org/wiki/2019%E2%80%9320_coronavirus_pandemic
|
||||
[2]: https://nextcloud.com/
|
||||
[3]: https://itsfoss.com/cloud-services-linux/
|
||||
[4]: https://www.youtube.com/c/itsfoss?sub_confirmation=1
|
||||
[5]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/03/nextcloud_files.png?ssl=1
|
||||
[6]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/03/nextcloud_video_call.jpg?ssl=1
|
||||
[7]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/03/calendar_nextcloud.jpeg?ssl=1
|
||||
[8]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/03/nextcloud_kanban_project_management_app.jpeg?ssl=1
|
||||
[9]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/03/passman.png?fit=800%2C389&ssl=1
|
||||
[10]: https://apps.nextcloud.com/apps/news
|
||||
[11]: https://apps.nextcloud.com/apps/passwords
|
||||
[12]: https://apps.nextcloud.com/
|
||||
[13]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/03/nextcloud-feature.jpg?ssl=1
|
||||
[14]: https://www.linode.com/?r=19db9d1ce8c1c91023c7afef87a28ce8c8c067bd
|
||||
[15]: https://m.do.co/c/d58840562553
|
||||
[16]: https://nextcloud.com/signup/
|
||||
[17]: https://nextcloud.com/enterprise/
|
||||
[18]: https://nextcloud.com/pricing/
|
||||
[19]: https://slack.com/
|
||||
[20]: https://trello.com/
|
||||
[21]: https://itsfoss.com/ghost-3-release/
|
||||
[22]: https://itsfoss.com/torvalds-remote-work-advice/
|
||||
@@ -1,233 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Access control lists and external drives on Linux: What you need to know)
|
||||
[#]: via: (https://opensource.com/article/20/3/external-drives-linux)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
Access control lists and external drives on Linux: What you need to know
|
||||
======
|
||||
Learn how to use external drives correctly on Linux.
|
||||
![Penguin driving a car with a yellow background][1]
|
||||
|
||||
While cloud storage offers many advantages, there's nothing quite like having your data on a physical hard drive. When you save data to a drive, you know exactly where your data is, and it's always available when you need it. When you save data to an external portable drive like a USB thumb drive, it's even better—not only do you know where your data is, but you can take your data with you everywhere you go. If you're new to [Linux][2], or you're trying to use a Linux file system on an external drive, you might find external drives confusing, being prone to permission errors or conflicts, or even losing metadata.
|
||||
|
||||
There are two "right" answers to this:
|
||||
|
||||
### ExFAT
|
||||
|
||||
Formerly, ExFAT was a file system fraught with legal threats from Microsoft because they own the code. They've sued companies and organizations before to defend their ownership of FAT, so it was commonly feared that they could do the same over ExFAT. However, recently. Microsoft made the specifications for ExFAT open source. They didn't provide a driver, unfortunately, but there's an existing drive to make it function on Linux, and, now that developers have access to the full specs, improvements are inevitable.
|
||||
|
||||
The advantage of ExFAT is that it's cross-platform (Windows, Mac, and many portable devices use it), and it's designed without the overhead of file permissions. You can attach a drive formatted as ExFAT to any computer, and all files are available to anyone. Whether that's good or bad depends on your use case, but for portable media, that's often exactly the intent.
|
||||
|
||||
### Access control lists (ACL)
|
||||
|
||||
If you prefer to use a Linux file system on your portable drive, then you can do that, but to make sharing files seamless, you should use access control lists (ACL).
|
||||
|
||||
When you create a file or directory on a drive, there are defaults on your system determining what file permissions it gets. For most cases, those defaults make sense—when you create a file in your home directory, you probably don't want other users to have access to that file. However, when you're creating a file on an external drive, there's a high likelihood that it's because you need to share that file with someone else (even if that someone is you on another computer).
|
||||
|
||||
You can override default permissions for file viewing with an ACL, and you can control default file creation mode by setting a sticky bit. An ACL is a layer of security policies in the extended attributes of directories and files. It allows you to specify exceptions to what the file system permissions indicate. Most notably, this allows you to transcend the single-owner and single-group model of traditional UNIX permissions.
|
||||
|
||||
For instance, while the **seth** (ID 1000) account might own a directory created on my desktop, **seth** (ID 500) on my laptop does not, because the user IDs are different.
|
||||
|
||||
The same could be true for a group. If a directory with group ID 1000 is assigned to a directory on one computer, then a group with an ID 500 or 10922 doesn't have access to it on another computer. But an ACL can add secondary owners and groups to directories and files.
|
||||
|
||||
#### View the current ACL
|
||||
|
||||
Any directory and file on any common Linux filesystem has ACL rules by default. They're stored in extended attributes, a kind of metadata that you don't normally see.
|
||||
|
||||
You can view them in the terminal:
|
||||
|
||||
|
||||
```
|
||||
$ getfacl ./example
|
||||
# file: /run/media/drive/example
|
||||
# owner: seth
|
||||
# group: users
|
||||
user::rwx
|
||||
group::rwx
|
||||
other::r--
|
||||
```
|
||||
|
||||
The commented lines are just for your reference; they tell you the path, and the owner and group, of the file or directory you're viewing information about. The next lines display the rules applied to the file or directory. In this example, the user permissions are set to **rwx**, the group to **r-x**, and other to **r-x**. These permissions are reflected by a normal filesystem list:
|
||||
|
||||
|
||||
```
|
||||
$ ls -lA /run/media/drive
|
||||
drwxrwxr-- 26 seth users 4096 Jan 16 21:04 example
|
||||
$ id
|
||||
uid=1000(seth) gid=100(users) groups=100(users)...
|
||||
```
|
||||
|
||||
As long as user **seth** (UID 1000) or a member of **group** (GID 100) interacts with the **example** directory, full access is granted. Any other account, however, has only read (**r**) permission.
|
||||
|
||||
#### Setting an ACL
|
||||
|
||||
To modify an ACL, you use the **setfacl** command or use a file manager with ACL support. You can be very specific or very generic when setting your ACL.
|
||||
|
||||
To just modify the filesystem permission settings, you can use either **chmod** or **setfacl**. This is a very generic ACL setting because you're not adding anything to the permissions already available to UNIX from the filesystem specification.
|
||||
|
||||
|
||||
```
|
||||
$ setfacl --modify g::r example
|
||||
$ getfacl ./example | grep "group::"
|
||||
group::r--
|
||||
$ ls -l . | grep example
|
||||
drwxr--r-- 26 seth users 4096 Jan 16 21:04 example
|
||||
```
|
||||
|
||||
The same effect is available through **chmod**:
|
||||
|
||||
|
||||
```
|
||||
$ chmod g+x example
|
||||
$ getfacl ./example | grep "group::"
|
||||
group::r-x
|
||||
$ ls -l . | grep example
|
||||
drwxr-xr-- 26 seth users 4096 Jan 16 21:04 example
|
||||
```
|
||||
|
||||
#### Adding users and groups
|
||||
|
||||
To really benefit from an ACL is to use it for permissions outside the scope of native UNIX permissions. If I'm logged into my desktop as **seth** with user ID 1000, and I know that a directory on my portable drive needs to be usable by **seth** with ID 500 on my laptop, then just declaring **seth** as owner isn't enough because the user IDs aren't the same.
|
||||
|
||||
You can add a user or user ID to an access control list:
|
||||
|
||||
|
||||
```
|
||||
$ setfacl --modify u:500:rwx example
|
||||
$ getfacl example
|
||||
# file: /run/media/drive/example
|
||||
# owner: seth
|
||||
# group: users
|
||||
user::rwx
|
||||
user:500:rwx
|
||||
[...]
|
||||
```
|
||||
|
||||
A new entry, specific to user ID 500, has been added to the list. Attaching the drive to another Linux or UNIX computer now allows the user with ID 500 to access the **example** folder.
|
||||
|
||||
You can also add users by account name, or groups by either group name or group ID. The IDs are what really count with permissions, though, so if you're in a mixed environment (RHEL servers and Elementary clients, for example), you should verify the user IDs and group IDs lurking behind accounts that seem, on the surface, identical.
|
||||
|
||||
#### Setting default ACL rules
|
||||
|
||||
If you treat access control as a one-time setting, you'll quickly run into problems once your different user accounts start creating files and directories. Any new file or directory created by each user inherits the system's default permissions (and ACL). This means that once laptop user **seth** with ID 500 creates a file in a directory, it could be off-limits to desktop user **seth** with ID 1000 because the owner of the file is set to UID 500.
|
||||
|
||||
A default ACL can be applied to directories so that files and subdirectories created within them inherit the parent ACL. You can set the default ACL of a directory with the **–default** option:
|
||||
|
||||
|
||||
```
|
||||
$ setfacl --default --modify u:500:rwx example
|
||||
$ setfacl --default --modify u:1000:rwx example
|
||||
$ getfacl --omit-header example
|
||||
user::rwx
|
||||
user:500:rwx
|
||||
group::rw-
|
||||
mask::rwx
|
||||
other::r-x
|
||||
default:user::rwx
|
||||
default:group::rw-
|
||||
default:group:500:rwx
|
||||
default:group:1000:rwx
|
||||
default😷:rwx
|
||||
default:other::r-x
|
||||
```
|
||||
|
||||
When a user creates a new directory within the **example** directory, the inherited ACL is the same as its parent:
|
||||
|
||||
|
||||
```
|
||||
$ cd example
|
||||
$ mkdir penguins
|
||||
$ getfacl --omit-header penguins
|
||||
user::rwx
|
||||
group::rw-
|
||||
group:500:rwx
|
||||
group:1000:rwx
|
||||
mask::rwx
|
||||
other::r-x
|
||||
default:user::rwx
|
||||
default:group::rw-
|
||||
default:group:500:rwx
|
||||
default:group:1000:rwx
|
||||
default😷:rwx
|
||||
default:other::r-x
|
||||
```
|
||||
|
||||
This means that any directory or file created inherits the same ACL, so neither user 500 or 1000 are ever excluded from access.
|
||||
|
||||
#### Pragmatic ACL for external drives
|
||||
|
||||
When using a Linux filesystem for external drives, the easy method of ensuring it works with all the users who expect to use the portable drive is to set an ACL on a single top-level directory.
|
||||
|
||||
For instance, assume you have formatted a USB drive called **mydrive** as an ext4 filesystem. You want your account on your laptop and your desktop, as well as your colleague Alice, to be able to access the files.
|
||||
|
||||
First, create a directory at the top level of the drive:
|
||||
|
||||
|
||||
```
|
||||
$ mkdir /mnt/mydrive/umbrella
|
||||
```
|
||||
|
||||
Then apply an ACL to the top-level directory to grant all-important users access:
|
||||
|
||||
|
||||
```
|
||||
$ setfacl --modify \
|
||||
u:500:rwx,u:1000:rwx,u:alice:rwx \
|
||||
/mnt/mydrive/umbrella
|
||||
```
|
||||
|
||||
Finally, apply a default ACL so that all directories and files created within the top-level directory **umbrella** inherit the same default ACL (note that this command uses the short version of **–modify**):
|
||||
|
||||
|
||||
```
|
||||
$ setfacl --default -m u:500:rwx,u:1000:rwx,u:alice:rwx \
|
||||
/mnt/mydrive/umbrella
|
||||
```
|
||||
|
||||
#### Applying defaults to an existing system
|
||||
|
||||
If you need to apply ACL settings to many files that already exist, you can accomplish that with the **find** command.
|
||||
|
||||
First, find all directories and apply ACL rules:
|
||||
|
||||
|
||||
```
|
||||
$ find /mnt/mydrive/umbrella -type d | \
|
||||
parallel --max-args=6 setfacl \
|
||||
--default -m u:500:rwx,u:1000:rwx,u:alice:rwx
|
||||
```
|
||||
|
||||
It's not wise to indiscriminately set all file permissions to executable, so next, find all files and set permissions to **re**. Files that require an executable bit can be set manually or by file extension:
|
||||
|
||||
|
||||
```
|
||||
$ find /mnt/mydrive/umbrella -type f | \
|
||||
parallel --max-args=6 setfacl \
|
||||
--default -m u:500:rw,u:1000:rw,u:alice:rw
|
||||
```
|
||||
|
||||
Adjust the logic of these commands to suit your individual need (don't run a command that removes the executable bit on **/usr**, for instance, or on a directory containing nothing but executable programs).
|
||||
|
||||
### External drives
|
||||
|
||||
Don't let confusion around external drives on Linux get the best of you, and don't limit yourself to traditional UNIX permissions. Put access control lists to work for you, and feel free to use native journaled Linux filesystems on your portable drives.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/3/external-drives-linux
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/car-penguin-drive-linux-yellow.png?itok=twWGlYAc (Penguin driving a car with a yellow background)
|
||||
[2]: https://opensource.com/resources/linux
|
||||
@@ -1,117 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How does kanban relate to DevOps?)
|
||||
[#]: via: (https://opensource.com/article/20/4/kanban-devops)
|
||||
[#]: author: (Willy-Peter Schaub https://opensource.com/users/wpschaub)
|
||||
|
||||
How does kanban relate to DevOps?
|
||||
======
|
||||
Reduce waste, optimize the flow of value, and continuously deliver value
|
||||
to delighted users.
|
||||
![two women kanban brainstorming and brainmapping with post-it notes on a whiteboard ][1]
|
||||
|
||||
Kanban is nothing new; in fact, it predates most readers of this article. Its age becomes apparent when we add the year Toyota introduced kanban in its main plant machine shop (1953) to the timeline image from our [analyzing the DNA of DevOps][2] article.
|
||||
|
||||
![DevOps timeline][3]
|
||||
|
||||
I have intuitively been using kanban, in one form or the other, for more than two decades to track personal plans, engineering projects, and digital transformations. Only in the past few weeks have I pondered the origins, power, and synergy of kanban with other frameworks and systems, while introducing teams to kanban and helping them embrace it as a powerful system in our common engineering system.
|
||||
|
||||
### What is kanban?
|
||||
|
||||
Kanban means "visual signal" and has its roots in the Toyota manufacturing industry. It was developed by [Taiichi Ohno][4] to improve manufacturing efficiency. When we jump a few decades into the future, kanban complements agile and lean, often used with frameworks such as scrum, Scaled Agile Framework, and Disciplined Agile to visualize and manage work.
|
||||
|
||||
![Kanban complements agile and lean][5]
|
||||
|
||||
You can explore the many interpretations of kanban on the internet, in books, and in vibrant discussions with other engineers who have embraced the system. In the context of our common collaboration and engineering system, kanban delivers four pivotal practices:
|
||||
|
||||
* **Visualize work:** We visualize all work and look for triggers such as cards turning **red** when the work they represent is blocked or has been dormant for more than two days.
|
||||
* **Limit work in progress:** We agree on and enforce (soft) work-in-progress limits to encourage reduced batch sizes and manage queue lengths.
|
||||
* **Focus on flow:** We _pull_ not push work, which helps us to defer commitment until we meet our definition of done (_DoD_) and we have the capacity to commit to the next _activity_.
|
||||
* **Continuous improvement:** It is important to measure work from when it enters our backlog, how long it takes to get through the process (lead time), and how efficient we are working (cycle/lead time). This enables us to continuously inspect and improve how we work and track progress.
|
||||
|
||||
|
||||
|
||||
![Kanban practices and terminology][6]
|
||||
|
||||
We use colorful, visual cards to represent activities that flow through one or more _activities_ in one of many _swim lanes_. Each kanban column represents an activity, and each swim lane represents a person, group, or another bucket to segment the cards. There are no rules for the color of the cards, but **red** typically signals a problem. But remember to combine color with a meaningful icon to visualize special states for users who are color-blind.
|
||||
|
||||
> "_We should defer commitment until our Definition of Ready (DOR) is met so that we can ensure that our Definition of Done (DOD) is achieved sooner and with high quality. I like the two distinct terms (DOR and DOD) because the [project owner] should be accountable for the DOR while the team can take ownership of the DOD_." —[Mathew Mathai][7]
|
||||
|
||||
I often use this analogy to explain the difference between _lead_ and _cycle_ time to new teams: Imagine you walk into a restaurant. You sit down, study the menu, and decide what you would like to drink and eat. When the waiter takes your order, the _lead_ cycle time starts ticking. When the bar starts pouring your favorite potion and the kitchen starts preparing your meal, the _cycle_ time starts ticking. As the order arrives at your table, both the lead and cycle time are stopped if (and only if) you are satisfied.
|
||||
|
||||
Therefore, the _lead_ time measures how long you, the customer, had to wait until you received your order. The _cycle_ time measures the process time of an activity to prepare your order. From a customer perspective, the _lead_ time is important.
|
||||
|
||||
It is important to _make your policies explicit_, such as when you start measuring lead and cycle times. Some customers start their "impatience" clock when they enter the restaurant, while others start the clock when they place their order. In both cases, they need to understand how you measure your flow to avoid misunderstandings, unfeasible expectations, and disappointment.
|
||||
|
||||
This image is extracted from one of our information transfer posters, and it summarizes key learnings when we started adopting the kanban system.
|
||||
|
||||
![Key kanban learnings][8]
|
||||
|
||||
### What about DevOps?
|
||||
|
||||
In _[Using PowerShell to automate Linux, macOS, and Windows processes][9]_, we briefly introduced value-stream mapping. It enables us to measure individual and total lead times, cycle times, efficiency, and quality and unearth different activities, groups, and silos that cancel out each other.
|
||||
|
||||
![value-stream mapping][10]
|
||||
|
||||
You will notice a similarity between the kanban board and the value-stream mapping images. Both _visualize_ and _focus_ on the flow of activities represented by individual cards pulled across a visual board.
|
||||
|
||||
![Continuous delivery pipeline][11]
|
||||
|
||||
Continuous flow and efficiency are core to a healthy DevOps mindset. It transforms into a continuous delivery pipeline, as shown above, which unites different teams, such as business, development, security, and quality assurance, to implement ideas from ideation to production. Continuously measuring and streamlining the delivery pipeline not only helps improve the flow of value, but also the quality of value.
|
||||
|
||||
It should be evident that (similar to kanban) the focus here is on flow. Flipping back and forth between activities is frowned upon in kanban and impractical with continuous delivery pipelines. It reminds me of a recent whiteboard discussion where we discussed the challenge of visualizing and managing the flow of work that requires two teams.
|
||||
|
||||
![Dividing a job between two teams][12]
|
||||
|
||||
As shown here, we slice a job that requires team X to perform activities, then team Y, and again team X, into three stories. The three stories are visualized by three cards on two kanban boards, flowing from A to B to C, with clear ownership by team X and Y, which we can measure independently as lead and cycle time.
|
||||
|
||||
We are drifting into another exciting topic of flow optimization … let's get back to the original question.
|
||||
|
||||
### What is the relationship between kanban and DevOps?
|
||||
|
||||
[Donovan Brown][13] defines DevOps as "_the union of people, process, and products to enable continuous delivery of value to our end users._"
|
||||
|
||||
When we unpack this definition, we realize that the core of the DevOps [mindset][14] is to continuously deliver value and delight our customers.
|
||||
|
||||
* _"Feedback from stakeholders is essential."_
|
||||
* _"Improve beyond the limits of today's processes."_
|
||||
* _"No new silos to break down silos."_
|
||||
* _"Knowing your customers means cross-organization collaboration."_
|
||||
* _"Inspire adoption through enthusiasm."_
|
||||
|
||||
|
||||
|
||||
The kanban system helps us visualize and improve the efficiency of value delivery, resulting in delighted customers. I argue that if you are comfortable with kanban, you will enjoy the full benefits of DevOps through _visualization_, _flow improvement_, _feedback_, and _continuous innovation_.
|
||||
|
||||
We have collaboration at its finest—_synergy_**,** or is that _symbiosis_?
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/4/kanban-devops
|
||||
|
||||
作者:[Willy-Peter Schaub][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/wpschaub
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/whiteboard-brainstorming-brainmapping-design-thinking-postits-kanban.png?itok=Is2Tg1Jk (Brainstorming with post-it notes on a whiteboard)
|
||||
[2]: https://opensource.com/article/18/11/analyzing-devops
|
||||
[3]: https://opensource.com/sites/default/files/uploads/devops-timeline.png (DevOps timeline)
|
||||
[4]: https://en.wikipedia.org/wiki/Taiichi_Ohno
|
||||
[5]: https://opensource.com/sites/default/files/uploads/kanban-agile-lean-devops.png (Kanban complements agile and lean)
|
||||
[6]: https://opensource.com/sites/default/files/uploads/kanban-practices-terms.png (Kanban practices and terminology)
|
||||
[7]: https://opensource.com/users/anicheinc
|
||||
[8]: https://opensource.com/sites/default/files/uploads/kanban-key-learnings.png (Key kanban learnings)
|
||||
[9]: https://opensource.com/article/20/2/devops-automation
|
||||
[10]: https://opensource.com/sites/default/files/uploads/value-stream-mapping.png (value-stream mapping)
|
||||
[11]: https://opensource.com/sites/default/files/uploads/cd-pipeline.png (Continuous delivery pipeline)
|
||||
[12]: https://opensource.com/sites/default/files/uploads/splitting-jobs.png (Dividing a job between two teams)
|
||||
[13]: https://www.donovanbrown.com/post/what-is-devops
|
||||
[14]: https://opensource.com/article/19/5/values-devops-mindset
|
||||
@@ -1,85 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Building a sensing prosthetic with the Raspberry Pi)
|
||||
[#]: via: (https://opensource.com/article/20/4/raspberry-pi-sensebreast)
|
||||
[#]: author: (Kathy Reid https://opensource.com/users/kathyreid)
|
||||
|
||||
Building a sensing prosthetic with the Raspberry Pi
|
||||
======
|
||||
SenseBreast is an early prototype of a sensing mastectomy prosthetic
|
||||
based on open hardware.
|
||||
![Open source doctor.][1]
|
||||
|
||||
_Content advisory: this article contains frank discussions of breast cancer._
|
||||
|
||||
What's the first question you ask your surgeon when you're discussing reconstruction options after breast cancer?
|
||||
|
||||
"How many USB ports can you give me?" is probably not the one that comes to mind for many people!
|
||||
|
||||
Although the remark was said jokingly, it sparked a thread that would ultimately become [SenseBreast][2]—an early prototype of a sensing mastectomy prosthetic, based on open hardware.
|
||||
|
||||
### How did SenseBreast come about?
|
||||
|
||||
All technology has a history—an origin story of experimentation, missteps, successes, setbacks, and breakthroughs. SenseBreast is no different. SenseBreast was developed as a term project for the Masters of Applied Cybernetics—a highly selective course at the Australian National University's [3A Institute][3]. The mission of the 3Ai is to bring artificial intelligence and cyber-physical systems safely, responsibly, and sustainably to scale. The purpose of the assignment was to explore the nexus between the electronic, virtual world, and the physical, tactile world.
|
||||
|
||||
### What is SenseBreast?
|
||||
|
||||
SenseBreast combines two distinct elements: a cyber component—electronics, sensors, and storage for gathering data, and a physical component—a breast form designed to be worn inside a mastectomy bra. SenseBreast is a rudimentary cyber-physical system. In cyber-physical systems, physical and software components are deeply intertwined and interact in different ways depending on context.
|
||||
|
||||
The SenseBreast draws on a rich heritage of open source hardware and software. Based on the Raspberry Pi 3B+, it uses the Debian-flavored Raspbian operating system, Python to interact with the onboard sensors, and d3.js to visualize the data that the sensors generate.
|
||||
|
||||
Early versions of the SenseBreast used the SenseHAT, but in the true spirit of open source collaboration, I partnered with Australian open source luminary Jon Oxer to develop a custom SenseBreast board. This contains an inertial motion unit (IMU) and temperature, humidity, and pressure sensors, just like the SenseHAT, but in addition, it contains the BME680 volatile gas sensor and a breakout for a heart rate monitor.
|
||||
|
||||
![SenseBreast open hardware board developed by Jon Oxer and Kathy Reid][4]
|
||||
|
||||
SenseBreast is wearable tech, so the physical form of the cyber-physical system is also important. Factors like comfort, texture, and fit in clothing are important in the design of wearables because technology isn't better unless it's better for people! The early attempts at building a housing for SenseBreast were spectacular failures; in fact, the very first iteration was put together using acrylic render and linen cloth, and held together with paper clips—in true hacker style! It wasn't comfortable to wear at all, but it served as a proof point for further exploration.
|
||||
|
||||
![First attempt at creating a breast form using acrylic render covered in linen cloth][5]
|
||||
|
||||
Later iterations used a different approach. This involved taking a cast of a breast, using quick-dry silicone supported by a plaster cast. The resulting mold was then used with slow-setting silicone to create a true-to-life shape. A recess was carved into the form to house the electronic components, and an additional silicone layer was added to protect the wearer's skin from contact with electronics.
|
||||
|
||||
### What did we learn from SenseBreast?
|
||||
|
||||
The key learning from SenseBreast is that data is partial. It only tells part of a story. It can be misleading and untrustworthy, which makes the decisions based on that data unreliable too. For example, the sensor data gathered by SenseBreast was affected by how hard the CPU was working. The graph below plots a sequence of 5 minutes of data from SenseBreast, just after the device has booted. You can see that the temperature decreases over time; this is because the CPU has to work harder as the Raspberry Pi boots, and then cools down after the boot operations are completed.
|
||||
|
||||
![Data visualisation of the readings in SenseBreast using the d3.js library][6]
|
||||
|
||||
These sorts of learnings have implications on a broader scale.
|
||||
|
||||
What if the SenseBreast were not an open source device, but a commercial wearable that stored data about me? What if part of the business model of that company was to sell the data that was harvested? What if my health insurer had access to that data? Or prospective employers? Now, more than ever, it's important that we have private, open solutions for sensing data about ourselves.
|
||||
|
||||
### What's next for the SenseBreast project?
|
||||
|
||||
The SenseBreast is a very early prototype, but it has the potential to develop through many different arcs. It could be used to assess range of movement post-surgery, to research how different garments and fabrics adjust to temperature and humidity, and to identify correlations between ambient air pressure and conditions such as lymphoedema. The path SenseBreast takes will be dependent on the passion, needs, and dedication of the incredible global open source community.
|
||||
|
||||
You can learn more about SenseBreast at [https://sensebreast.org][2] and see my presentation at [linux.conf.au][7] 2020 [here][8].
|
||||
|
||||
The code for SenseBreast is available on [GitHub][9].
|
||||
|
||||
Health IT has been surprisingly unwilling to deeply support open source software. Despite the huge...
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/4/raspberry-pi-sensebreast
|
||||
|
||||
作者:[Kathy Reid][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/kathyreid
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/osdc_520x292_opensourcedoctor.png?itok=fk79NwpC (Open source doctor.)
|
||||
[2]: https://sensebreast.org/
|
||||
[3]: https://3ainstitute.cecs.anu.edu.au/
|
||||
[4]: https://opensource.com/sites/default/files/uploads/49427571178_bb5df37c3a_c.jpg (SenseBreast open hardware board developed by Jon Oxer and Kathy Reid)
|
||||
[5]: https://opensource.com/sites/default/files/uploads/49641040471_6d0cc91619_c.jpg (First attempt at creating a breast form using acrylic render covered in linen cloth)
|
||||
[6]: https://opensource.com/sites/default/files/uploads/49640513813_5a7d63803a_c.jpg (Data visualisation of the readings in SenseBreast using the d3.js library)
|
||||
[7]: http://linux.conf.au
|
||||
[8]: https://www.youtube.com/watch?v=G3QfZ11DCpc.
|
||||
[9]: https://github.com/KathyReid/sensebreast
|
||||
@@ -1,92 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Love or hate chat? 4 best practices for remote teams)
|
||||
[#]: via: (https://opensource.com/article/20/4/chat-tools-best-practices)
|
||||
[#]: author: (Jen Wike Huger https://opensource.com/users/jen-wike)
|
||||
|
||||
Love or hate chat? 4 best practices for remote teams
|
||||
======
|
||||
Plus, learn about a few open source alternatives for chat.
|
||||
![Chat via email][1]
|
||||
|
||||
Chat is a part of most people's daily lives, especially if you work in tech, and especially if you work with teammates located in different parts of the world. It can be a great way to achieve these goals:
|
||||
|
||||
* **to connect**; to share with teammates on a personal level
|
||||
* **to get work done**; to communicate with teammates about work in progress
|
||||
* **to share**; to give notes and feedback from experiences, meetings, and interactions outside of the group that may be relevant to your work or interests
|
||||
|
||||
|
||||
|
||||
I encourage you to explore [open source alternatives to chat][2] like [Mattermost][3], [Rocket.Chat][4], and [Riot][5].
|
||||
|
||||
### To chat or not to chat, that is the question
|
||||
|
||||
First, it's important to make time to have a discussion with each member of your team focused on answering whether they are comfortable with using a chat platform to keep in touch throughout the workday. Some people enjoy chat and see it as a vital part of their workday, getting things done and communicating with teammates who they rely on to get that work done and move forward with projects. Others struggle with chat as a way of getting work done and prefer to use it when they feel like having more casual conversations with teammates on topics less focused on work and more on social interaction and personal sharing. Some people wish chat would burn in a fire.
|
||||
|
||||
Gather these opinions and talk through these feelings with each person. You can do this as a group or one-on-one if that feels more appropriate.
|
||||
|
||||
Why? Because communication is important and always will be, and your team will find a way to chat no matter what you do. We're human, and need various levels and types of interaction with each other throughout our days and lives. And when it comes to our work colleagues, it's helpful to put some structure in place to guide your team.
|
||||
|
||||
### Best practices for team chat
|
||||
|
||||
If you have decided to use chat in some form, the next step is to place structure around when and how to use it and **not** use it. These best practices work well for teams who are working remotely and at home, as well as in the office.
|
||||
|
||||
**1\. Create rooms and threads to focus your conversations.**
|
||||
|
||||
My team has a room for each of our sub-teams who work on a particular project together. We also have an at-large room for all of us to banter and share.
|
||||
|
||||
Additionally, we use threads to focus on one topic at a time which is helpful when you have several to dozens of teammates in one room together. It helps conversations to continue and not stop prematurely because they were lost in the mix of other conversations.
|
||||
|
||||
**2\. Decide when your team will be signed in and available to talk.**
|
||||
|
||||
Is it throughout the workday (whatever hours those are for you), during a set timeframe, or as desired?
|
||||
|
||||
My team has set the expectation that they will be signed in and available to chat at some point during the workday **about work-related topics**, and that at that time they will check for and respond to messages that were sent to them while they were away. So, we are using it as an asynchronous way to communicate about work.
|
||||
|
||||
For us, asynchronous chat helps us plan and schedule each day how we see fit with the goal of being productive and serving our project in the best we can _that day_.
|
||||
|
||||
If a teammate does **not** plan on signing in and responding to messages one day, that is OK, and we set the expectation that they will send a message to let the team know. For my team, almost no communication is wrong (see guideline #4), but it should be communicated. We also review our schedules for the following week in a team meeting the week before so we know when someone will be away from their desk, not working, or blocking out a chunk of time for a project.
|
||||
|
||||
**3\. Decide when your teammates are responsible for responding (and when they are not).**
|
||||
|
||||
Use @ mentions if you want someone to see and respond to your question or comment in chat. Don't expect them to be watching every thread and conversation.
|
||||
|
||||
And I would recommend that you take it a step further and define when teammates should be responsible for responding and when they should not. This type of decision is meant to free you and your teammates, not hold you down. The more you understand the expectations, the freer you are to operate within the same understood universe. When you are unsure of the rules, you may act and make decisions in fear or trepidation instead, like staying signed in to chat all day when you really just need to block it out to get something done.
|
||||
|
||||
Our team has decided that it's nice if you can respond in chat when you are mentioned, but if you don't that is OK. Perhaps you were AFK during that time and lost track of the notification. For us, if you definitely want a response to something from someone, send them an email.
|
||||
|
||||
**4\. Communicate clearly and with kindness.**
|
||||
|
||||
The way we interpret messages when we are chatting via text is different than when we are chatting verbally, in-person or over video.
|
||||
|
||||
My team uses a lot of humor, emojis, and clear, concise messages to chat with each other.
|
||||
|
||||
We also hold weekly in-person or video conference meetings so that we can get to know each other better. The more you trust someone, the easier it is to give them the benefit of the doubt when you're confused by a message and the better you are at understanding what they are saying and what their intention is behind the text coming through to you.
|
||||
|
||||
### Signing off
|
||||
|
||||
What best practices does your team use? Do you love or hate chat, and why?
|
||||
|
||||
For all kinds of teams today, chat is a special part of how we stay connected, working, and sharing with each other. Finding ways to do that in a healthy and committed way is part of everyone's responsibility.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/4/chat-tools-best-practices
|
||||
|
||||
作者:[Jen Wike Huger][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jen-wike
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/email_chat_communication_message.png?itok=LKjiLnQu (Chat via email)
|
||||
[2]: https://opensource.com/alternatives/slack
|
||||
[3]: https://mattermost.com/
|
||||
[4]: https://rocket.chat/
|
||||
[5]: https://riot.im/app/
|
||||
@@ -1,84 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (GNOME Announces Community Engagement Challenge Offering up to $65,000 in Rewards)
|
||||
[#]: via: (https://itsfoss.com/gnome-community-engagement-challenge/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
GNOME Announces Community Engagement Challenge Offering up to $65,000 in Rewards
|
||||
======
|
||||
|
||||
It’s always good to see several competitions or challenges trying to promote Free and Open-Source Software (FOSS) more than ever.
|
||||
|
||||
In a recent effort by GNOME with the help of [Endless][1], they announced the inaugural GNOME Community Engagement Challenge.
|
||||
|
||||
This Community Challenge is a part of their original announcement of [coding education challenge for which GNOME was granted $500,000 funding by Endless][2] last year.
|
||||
|
||||
The three-phase challenge aims to attract new developers to engage with FOSS and potentially create new/unique solutions that would gain more traction from the next-gen coders.
|
||||
|
||||
The challenge will involve up to $65,000 in cash prizes. Sounds exciting, right? Let’s take a look at some of the details involved in the challenge.
|
||||
|
||||
![][3]
|
||||
|
||||
### Why The GNOME Community Engagement Challenge?
|
||||
|
||||
In their official [press release][4], they mentioned their primary motive for the challenge:
|
||||
|
||||
> “Through the Challenge we hope to reach a diverse audience, to encourage beginning coders to get involved with the FOSS community to help ensure that free software is available long into the future,” said Neil McGovern, GNOME Foundation Executive Director. “What better way to do that than to reach out to the community itself to come up with creative ways to inspire the next generation?”
|
||||
|
||||
As Neil mentioned above, it’s definitely a good idea to reach out to more people (community) to look for creative ways to promote and work on FOSS projects that will leave a significant impact on the open-source community.
|
||||
|
||||
And, rewarding for the ideas in the form of a challenge will easily get the attention needed.
|
||||
|
||||
### Here’s How The Community Challenge Works
|
||||
|
||||
To quote the official announcement:
|
||||
|
||||
> The Challenge will ask entrants to devise creative ways to promote open-source software to coders typically in high school and college. How a submission will achieve this goal has deliberately been left open-ended to encourage unique, novel approaches.
|
||||
|
||||
So, there’s no particular constraint for the type of ideas or projects you can propose and submit. But, it would be wise to read the usual [terms and conditions][5] to know about the submission rules, eligibility, requirements, prize details, and more.
|
||||
|
||||
Here are the key information about the three phases of the challenge as per the announcement:
|
||||
|
||||
* The **first phase** of the Challenge asks entrants to submit a written proposal for their concept no later than **July 1, 2020**. Twenty entries will be chosen to move to the next round and receive **$1000 each**.
|
||||
* The **second phase** of the Challenge will require proof of concept, with four entries receiving **$5000** and moving onto the final round.
|
||||
* The final round will call for a deliverable end product, with the winner receiving **$15,000** and the second place finisher receiving **$10,000**.
|
||||
|
||||
|
||||
|
||||
They plan to announce the winner of the challenge in the spring of 2021.
|
||||
|
||||
You can take a look at their [challenge FAQ][6] and the [official webpage][7] for more details before starting to submit your entry on **April 9th**. The last date of submission is **July 1, 2020**.
|
||||
|
||||
Head to their website to get started and explore more about the challenge.
|
||||
|
||||
[GNOME Community Engagement Challenge][7]
|
||||
|
||||
### Wrapping Up
|
||||
|
||||
I think this is a perfect opportunity for developers to get started with FOSS projects that will end up rewarding them with a good amount of money and help the community at the same time.
|
||||
|
||||
What do you think about the community engagement challenge by GNOME? Feel free to let me know your thoughts in the comments below.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/gnome-community-engagement-challenge/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.endlessnetwork.com/
|
||||
[2]: https://itsfoss.com/endless-gnome-coding-education-challenge/
|
||||
[3]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/04/gnome-community-challenge.png?ssl=1
|
||||
[4]: https://www.gnome.org/news/2020/04/gnome-foundation-and-endless-launch-inaugural-community-engagement-challenge/
|
||||
[5]: https://www.gnome.org/challenge/terms/
|
||||
[6]: https://www.gnome.org/challenge/faq/
|
||||
[7]: https://www.gnome.org/challenge/
|
||||
@@ -1,152 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Print double-sided documents at home with this simple Bash script)
|
||||
[#]: via: (https://opensource.com/article/20/4/print-duplex-bash-script)
|
||||
[#]: author: (Jim Hall https://opensource.com/users/jim-hall)
|
||||
|
||||
Print double-sided documents at home with this simple Bash script
|
||||
======
|
||||
Use this script and save yourself the hassle and wasted paper of trying
|
||||
to manually load and print double-sided documents.
|
||||
![bash logo on green background][1]
|
||||
|
||||
We have a laser printer at home. This Hewlett Packard LaserJet Pro CP1525nw Color Printer is an older model, but it has been a great workhorse that prints reliably and in color. I [put it on our home network][2] a few years ago using our [Raspberry Pi][3] as a print server.
|
||||
|
||||
The LaserJet has been a great addition to my home office. Since [I launched my company][4] last year, I have relied on this little laser printer to print handouts and other materials for client meetings, workshops, and training sessions.
|
||||
|
||||
My only gripe with this printer is that it prints single-sided only. If you want to print double-sided, you need to set up a custom print job to do it yourself. That's inconvenient and requires manual steps. In LibreOffice, I need to specifically set up the print job to print the odd-numbered pages first, then reload the paper before printing the even-numbered pages on the other side—but in reverse order.
|
||||
|
||||
![LibreOffice print dialog][5]
|
||||
|
||||
If I need to print a PDF that someone has sent me, the process is the same. For a four-page document, I first need to print pages 1 and 3, then reload the paper and print pages 2 and 4 in reverse order. In the GNOME print dialog, you need to select "Page Setup" to print odd pages or even pages.
|
||||
|
||||
![Gnome print dialog][6]
|
||||
|
||||
![Gnome page setup][7]
|
||||
|
||||
Regardless of how I print, the overall process is to print the odd-numbered pages, reload the stack of printed pages into the paper tray, then print the even-numbered pages in reverse order. If I'm printing a four-page document, printing the even-numbered pages in reverse order means page 4 prints on the back of page 3 and page 2 prints on the back of page 1. Imagine my frustration in those few instances when I forgot to select the option to print in reverse order when printing the even-numbered pages and ruined a long print job.
|
||||
|
||||
Similarly, it's easy to forget how to deal with documents that have an odd number of pages. In a five-page document, you first print pages 1, 3, and 5. But when you reload the printed pages into the printer, you don't want page 5. Instead, you only want to load pages 1 and 3. Otherwise, page 4 will print on the back of page 5, page 2 will print on the back of page 3, and nothing gets printed on the back of page 1.
|
||||
|
||||
To make things easier and more reliable, I wrote a simple Bash script that automates printing duplex. This is basically a wrapper to print odd-numbered pages, remind me to reload the pages (and remove the last page if needed), then print the even-numbered pages.
|
||||
|
||||
Whenever I need to print a document as duplex, I first convert the document to PDF. This is very easy to do. In LibreOffice, there's a toolbar icon to export directly as PDF. You can also navigate under **File— Export As—Export as PDF** to do the same. Or in any other application, there's usually a **Save to PDF** feature. When in doubt, GNOME supports printing to a PDF file instead of a printer.
|
||||
|
||||
![Libre Office toolbar][8]
|
||||
|
||||
![Export as PDF][9]
|
||||
|
||||
### How it works
|
||||
|
||||
Once I've saved to PDF, I let my Bash script do the rest. This really just automates the **lpr** commands to make printing easier. It prints odd pages first, prompts me to reload the paper, then prints the even pages. If the document has an odd number of pages, it also reminds me to remove the last page when I reload the printed pages. It's pretty simple.
|
||||
|
||||
The only "programming" part of the script is determining the page count, and figuring out if that's an even or odd number. Both of those are easy to do.
|
||||
|
||||
To determine the page count, I use the **pdfinfo** command. This generates useful info about a PDF document. Here's some sample output:
|
||||
|
||||
|
||||
```
|
||||
$ pdfinfo All\ training\ -\ catalog.pdf
|
||||
Creator: Writer
|
||||
Producer: LibreOffice 6.3
|
||||
CreationDate: Fri Oct 18 16:06:07 2019 CDT
|
||||
Tagged: no
|
||||
UserProperties: no
|
||||
Suspects: no
|
||||
Form: none
|
||||
JavaScript: no
|
||||
Pages: 11
|
||||
Encrypted: no
|
||||
Page size: 612 x 792 pts (letter)
|
||||
Page rot: 0
|
||||
File size: 65623 bytes
|
||||
Optimized: no
|
||||
PDF version: 1.5
|
||||
```
|
||||
|
||||
That output is very easy to parse. To get the page count, I use an AWK one-line script to look for **Pages:** and print the second field.
|
||||
|
||||
|
||||
```
|
||||
`pages=$( pdfinfo "$1" | awk '/^Pages:/ {print $2}' )`
|
||||
```
|
||||
|
||||
To figure out if this is an odd or even number, I use the modulo (**%**) arithmetic operator to divide by two and tell me the remainder. The modulo of two will always be zero for an even number, and one for an odd number. I use this simple test to determine if the document has an odd number of pages, so I'll need to remove the last page before printing the rest of the document:
|
||||
|
||||
|
||||
```
|
||||
`if [ $(( $pages % 2 )) -ne 0 ] ; then`
|
||||
```
|
||||
|
||||
With that, writing the **print-duplex.sh** Bash script is a simple matter of calling **lpr** with the correct options to send output to my printer (**lpr -P "HP_LaserJet_CP1525nw"**), to print odd-numbered pages (**-o page-set=odd**) or even-numbered pages (**-o page-set=even**), and to print in reverse order (**-o outputorder=reverse**).
|
||||
|
||||
### Bash script
|
||||
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
# print-duplex.sh
|
||||
# simple wrapper to print duplex
|
||||
|
||||
cat<<EOF
|
||||
$1 ($pages pages)
|
||||
\-------------------------------------------------------------------------------
|
||||
Printing odd pages first
|
||||
Please wait for job to finish printing...
|
||||
\-------------------------------------------------------------------------------
|
||||
EOF
|
||||
|
||||
lpr -P "HP_LaserJet_CP1525nw" -o page-set=odd "$1"
|
||||
sleep $pages
|
||||
|
||||
cat<<EOF
|
||||
===============================================================================
|
||||
Put paper back into the printer in EXACT OUTPUT ORDER (face down in tray)
|
||||
then press ENTER
|
||||
===============================================================================
|
||||
EOF
|
||||
|
||||
pages=$( pdfinfo "$1" | awk '/^Pages:/ {print $2}' )
|
||||
|
||||
if [ $(( $pages % 2 )) -ne 0 ] ; then
|
||||
echo '!! Remove the last page - this document has an odd number of pages'
|
||||
fi
|
||||
|
||||
echo -n '>'
|
||||
read x
|
||||
|
||||
cat<<EOF
|
||||
\-------------------------------------------------------------------------------
|
||||
Printing even pages
|
||||
Please wait for job to finish printing...
|
||||
\-------------------------------------------------------------------------------
|
||||
EOF
|
||||
|
||||
lpr -P "HP_LaserJet_CP1525nw" -o page-set=even -o outputorder=reverse "$1"
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/4/print-duplex-bash-script
|
||||
|
||||
作者:[Jim Hall][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jim-hall
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/bash_command_line.png?itok=k4z94W2U (bash logo on green background)
|
||||
[2]: https://opensource.com/article/18/3/print-server-raspberry-pi
|
||||
[3]: https://opensource.com/resources/raspberry-pi
|
||||
[4]: https://opensource.com/article/19/9/business-creators-open-source-tools
|
||||
[5]: https://opensource.com/sites/default/files/uploads/print_dialog_-_libreoffice_0.png (LibreOffice print dialog)
|
||||
[6]: https://opensource.com/sites/default/files/uploads/print_dialog_-_gnome_0.png (Gnome print dialog)
|
||||
[7]: https://opensource.com/sites/default/files/uploads/print_dialog_-_gnome_-_page_setup.png (Gnome page setup)
|
||||
[8]: https://opensource.com/sites/default/files/uploads/toolbar_-_export_as_pdf_-_libreoffice.png (Libre Office toolbar)
|
||||
[9]: https://opensource.com/sites/default/files/uploads/file_-_export_as_pdf_-_libreoffice.png (Export as PDF)
|
||||
@@ -1,142 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Use Emacs Org mode to easily create LaTeX documents)
|
||||
[#]: via: (https://opensource.com/article/20/4/emacs-org-mode)
|
||||
[#]: author: (Peter Prevos https://opensource.com/users/danderzei)
|
||||
|
||||
Use Emacs Org mode to easily create LaTeX documents
|
||||
======
|
||||
You can use LaTeX for scientific and technical documents without all of
|
||||
the confusing commands and syntax you would normally need.
|
||||
![Filing cabinet for organization][1]
|
||||
|
||||
LaTeX is a powerful system, especially for writing scientific and technical documents. But writing documents in LaTeX can be confusing because you need to know a lot of commands, and your text is littered with backslashes, curly braces, and other syntax distractions. But being productive as a writer requires that you focus on the text's content instead of how it looks. Fortunately, the [GNU Emacs][2] Org mode extension makes it easy to write plain-text documents and seamlessly export them to LaTeX and PDF.
|
||||
|
||||
[Org mode][3] is a built-in Emacs extension that helps you keep notes, maintain to-do lists, manage projects, and author documents with a fast and effective plain-text system. Emacs also comes with [AUCTeX][4], an extensible package for writing TeX files in Emacs. AUCTeX has a preview module that shows the results of what you type, but I find it distracting because it draws my attention away from the document's content to its design. Writing text in Org mode is my preferred option because the source remains a plain-text file with minimal typesetting elements. The text is independent of its result because Org mode can export it to multiple formats, including LaTeX and PDF.
|
||||
|
||||
Emacs is known for being difficult to use with a steep learning curve. But Emacs is only difficult when you want to fine-tune the default settings. By following a minimalist approach to using the vanilla GNU Emacs, this article will get you quickly and easily on your way to writing beautiful documents without any complex configuration.
|
||||
|
||||
### First steps
|
||||
|
||||
Before you begin, [install Emacs][5] and a fully functioning version of [LaTeX][6] on your computer.
|
||||
|
||||
Next, you need to learn some conventions. In Emacs lingo, the abbreviation **C-c** means to enter **Ctrl+C** on your keyboard. The abbreviation **M-x** means **Alt+X**. The M stands for the mod key, which no longer exists in modern systems. The **S** prefix indicates the **Shift** key.
|
||||
|
||||
The **find-file** function, which you start with the **C-x C-f** keystroke combination, creates a new document or opens an existing document. Entering this function opens a dialog in the mini-buffer at the bottom of the screen, which is where Emacs communicates with the user. Type the name of the file you want to create or open into the mini-buffer. Emacs is sensitive to file extensions, so make sure that the name of your document ends in **.org**.
|
||||
|
||||
In Emacs speak, opening or creating a file is called ["visiting" a file][7]. Visiting a file means reading its contents into an Emacs buffer so that it is available for editing. Emacs generates a new buffer for each file you visit.
|
||||
|
||||
### Writing prose with Org mode
|
||||
|
||||
Once you're visiting a file, you can start typing your text the same way you would in any text editor or word processor. Some conventions: Begin the file with **#+TITLE:** to denote the title of the document and **#+AUTHOR** for your name. These options are used when exporting the file. Org mode recognizes a range of [export settings][8] to configure the output. For example, to suppress the table of contents, enter **#+OPTIONS: toc:nil**.
|
||||
|
||||
Org mode has its own Markdown-like conventions to format your document. [Headlines][9] start with one or more asterisks. Org mode can [collapse a headline][10] to render parts of it invisible with the **TAB** or **S-TAB** keys. You can make words ***bold***, **/italic/**, **_underlined_**, or **=verbatim=**. The Org manual describes the many options for [rich text][11].
|
||||
|
||||
One minor issue with plain-vanilla Emacs that you will quickly notice is it does not wrap lines at the end of the visible screen. Emacs has several line-wrapping functions, and [Visual Line mode][12] is the most useful for writing long-form text. To activate this mode, use **M-x** and enter **visual-line-mode** in the mini-buffer at the bottom of the screen. The **M-x** keyboard shortcut enables executing functions for which there is no direct keyboard shortcut.
|
||||
|
||||
Adding [images][13] is as easy as adding a link to the image file within double square brackets:
|
||||
|
||||
|
||||
```
|
||||
`[[file:path_to_image.png]]`
|
||||
```
|
||||
|
||||
Org has a great system for [formatting tables][14] in plain ASCII. Any line with **|** is considered part of a table. The vertical line is also the column separator. A line starting with **|-** is rendered as a horizontal rule, and rows before the first horizontal rule are header lines. A table might look like this in the source file:
|
||||
|
||||
|
||||
```
|
||||
| Name | id | Age |
|
||||
|-------+------+-----|
|
||||
| Peter | 1234 | 50 |
|
||||
| Sue | 4321 | 54 |
|
||||
```
|
||||
|
||||
Both images and tables are preceded with **#+CAPTION:** to add a [caption][15]. Advanced options are also available to control float placement and size of figures.
|
||||
|
||||
Emacs has extensive [editing functions][16] to make you more efficient when typing text. Spell checking, thesaurus, auto-completion, and an undo tree are just some of the tools that help you write efficiently.
|
||||
|
||||
### Adding LaTeX snippets to Org
|
||||
|
||||
In addition to the text itself, Org mode-text can include simple LaTeX commands, such as **\newpage**, within the text. Equations in standard LaTeX syntax are placed between dollar signs **$e^{i\pi} + 1 = 0$**. The **org-latex-preview** function (**C-c C-x C-l**) shows a [preview][17] of any LaTeX equations within the text buffer. Last, you can also add complete LaTeX snippets to insert complex content. The code has to be placed in an export block:
|
||||
|
||||
|
||||
```
|
||||
#+BEGIN_EXPORT latex
|
||||
\setlength{\unitlength}{1cm}
|
||||
\thicklines
|
||||
\begin{picture}(10,6)
|
||||
\put(2,2.2){\line(1,0){6}}
|
||||
\put(2,2.2){\circle{2}}
|
||||
\put(6,2.2){\oval(4,2)[r]}
|
||||
\end{picture}
|
||||
#+END_EXPORT
|
||||
```
|
||||
|
||||
### Exporting to LaTeX
|
||||
|
||||
Org mode includes a powerful export module to convert your files to many formats using the powerful [Pandoc][18] software. Start the export module with the **org-export-dispatch** function, which you can run with the **C-c C-e** keyboard shortcut. The dispatch will split your screen and provide a range of options.
|
||||
|
||||
First, Pandoc converts the Org mode file to a LaTeX file. Then you can choose to open the LaTeX file in a new buffer or save it as a file. Org mode can also directly render a PDF file, which you can view within Emacs or save to disk.
|
||||
|
||||
![Emacs with Org mode source and PDF preview][19]
|
||||
|
||||
### Advanced use
|
||||
|
||||
This article provides a first taste of writing prose in Org mode and LaTeX. Org mode has numerous configuration options to fine-tune your document or to change default settings.
|
||||
|
||||
By default, Org mode uses the article style to export documents, but you can change this with export settings. These settings can also be used to add commands to the document header, for example:
|
||||
|
||||
|
||||
```
|
||||
#+LATEX_CLASS: report
|
||||
#+LATEX_CLASS_OPTIONS: [a4paper]
|
||||
#+LATEX_HEADER: \usepackage{times}
|
||||
```
|
||||
|
||||
If you write scientific documents, the [org-ref][20] package by John Kitchin provides Org-mode modules for citations, cross-references, and bibliographies in Org mode and useful BibTeX tools to go with it.
|
||||
|
||||
The Org mode manual's [LaTex export][21] section provides a detailed discussion of the functionality available.
|
||||
|
||||
### Conclusion
|
||||
|
||||
Org mode is a perfect editor for writing LaTeX. The main advantage is that you lose the clutter of LaTeX syntax and can focus on the text. This comes at no cost because you can still add LaTeX code as much as you need, and you get access to the powerful editing functions in Emacs.
|
||||
|
||||
Using Org to write books and articles allows you to focus on the text as you combine two of the oldest and most powerful pieces of open source software.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/4/emacs-org-mode
|
||||
|
||||
作者:[Peter Prevos][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/danderzei
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/files_documents_organize_letter.png?itok=GTtiiabr (Filing cabinet for organization)
|
||||
[2]: https://opensource.com/article/20/3/getting-started-emacs
|
||||
[3]: https://orgmode.org
|
||||
[4]: https://www.gnu.org/software/auctex/
|
||||
[5]: https://www.gnu.org/software/emacs/
|
||||
[6]: https://www.latex-project.org/get/
|
||||
[7]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Visiting.html
|
||||
[8]: https://orgmode.org/manual/Export-Settings.html
|
||||
[9]: https://orgmode.org/manual/Headlines.html#Headlines
|
||||
[10]: https://orgmode.org/manual/Global-and-local-cycling.html#Global-and-local-cycling
|
||||
[11]: https://orgmode.org/manual/Markup-for-Rich-Contents.html#Markup-for-Rich-Contents
|
||||
[12]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Visual-Line-Mode.html
|
||||
[13]: https://orgmode.org/manual/Images.html
|
||||
[14]: https://orgmode.org/manual/Built_002din-Table-Editor.html#Built_002din-Table-Editor
|
||||
[15]: https://orgmode.org/manual/Captions.html#Captions
|
||||
[16]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Basic.html#Basic
|
||||
[17]: https://orgmode.org/manual/Previewing-LaTeX-fragments.html
|
||||
[18]: https://pandoc.org/
|
||||
[19]: https://opensource.com/sites/default/files/uploads/org-mode-latex-screenshot.png (Emacs with Org mode source and PDF preview.)
|
||||
[20]: https://github.com/jkitchin/org-ref
|
||||
[21]: https://orgmode.org/manual/LaTeX-Export.html#LaTeX-Export
|
||||
@@ -1,55 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How Kubernetes saved my desktop application)
|
||||
[#]: via: (https://opensource.com/article/20/4/kubernetes-desktop-application)
|
||||
[#]: author: (Chris Hermansen https://opensource.com/users/clhermansen)
|
||||
|
||||
How Kubernetes saved my desktop application
|
||||
======
|
||||
Keep this fix in mind if you have a broken Java desktop application but
|
||||
aren't a crypto expert.
|
||||
![Puzzle pieces coming together to form a computer screen][1]
|
||||
|
||||
Recently, fellow Opensource.com scribe James Farrell wrote a wonderful article entitled _[How Ansible brought peace to my home][2]_. In addition to the great article, I really liked the title, one of those unexpected phrases that I’m sure brought a smile to many faces.
|
||||
|
||||
I recently had a weird but positive experience of my own that begs a similar sort of unexpected label. I’ve been grappling with a difficult problem that arose when upgrading some server and networking infrastructure that broke a Java application I’ve been supporting since the early 2000s. Strangely enough, I found the solution in what appears to be a very informative and excellent article on Kubernetes, of all things.
|
||||
|
||||
Without further ado, here is my problem:
|
||||
|
||||
![][3]
|
||||
|
||||
I’m guessing that most readers will look at that message and think things like, "I hope there’s more info in the log file," or "I’m really glad I’ve never received a message like that."
|
||||
|
||||
Unfortunately, there isn’t a lot of info in the log file, just the same message, in fact. In an effort to debug this, I did three things:
|
||||
|
||||
1. I searched online for the message. Interestingly, or perhaps ominously, there were only 200 or so hits on this string, [one of which suggested][4] [turn][4][ing][4] [on more debugging output][4], which involved adding the setting
|
||||
|
||||
|
||||
```
|
||||
**-Djavax.net.debug=ssl:handshake:verbose**[/code] to the **java** command running the application.
|
||||
|
||||
2. I tried that suggestion, which resulted in a lot of output (good), most of which only vaguely made sense to me as I’m no kind of expert in the underlying bits of stuff like SSL. But one thing I did notice is that there was no information regarding a response from the server in the midst of all of that output;
|
||||
|
||||
3. So I searched some more.
|
||||
|
||||
|
||||
|
||||
|
||||
Another interesting part of this problem is that the code ran fine when executed by the Java command bundled in the OpenJDK, but failed with this error when using a customized runtime [created from the same OpenJDK in this way][5]. So the relatively modest number of apparently similar problems turned up from search #1 above were actually not all that relevant since they all seemed to be dealing mostly with bad SSL certificates on the server in conjunction with the PostgreSQL JDBC’s ability to check the server’s credentials.
|
||||
|
||||
I should also mention that it took me quite some time to realize that the problem was introduced by using the custom Java runtime, as I managed to check many other possibilities along the way (and indeed, I did fix a few minor bugs while I was at it). My efforts included things like getting the latest OpenJDK, checking and re-checking all the URLs in case one had a typo, and so forth.
|
||||
|
||||
As often happens, after putting the problem aside for a few hours, an idea occurred to me—perhaps I was missing some module in the customized Java runtime. While I didn’t receive any errors directly suggesting that problem, the observable fact that the standard OpenJDK environment worked while the custom one failed seemed to hint at that possibility. I took a quick look in the **jmods/** folder in the OpenJDK installation, but there are some 70 modules there and nothing jumped out at me.
|
||||
|
||||
But again, what seemed odd was, with debugging turned on (see #1 above), there was no indication of what the server would accept, just what the client mostly couldn’t offer, many lines like this:
|
||||
```
|
||||
`Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA`
|
||||
```
|
||||
So I was at least thinking by this time that maybe what was missing was the module that offered those kinds of cipher suites. So I started searching with strings like "jdbc crypto," and in the midst of that, the most unlikely article showed up: [Optimizing Kubernetes Services—Part 2: Spring Web][6], written by [Juan Medina][7]. Midway down the article, I spotted the following:
|
||||
|
||||
![][8]
|
||||
|
||||
Huh! Imagine that, his script is creating a custom Java runtime, just like mine. But he says he needs to add in manually the module **jdk.crypto.ec** in order t
|
||||
@@ -1,121 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Use this helpful Bash script when stargazing)
|
||||
[#]: via: (https://opensource.com/article/20/4/linux-astronomy)
|
||||
[#]: author: (Alan Formy-Duval https://opensource.com/users/alanfdoss)
|
||||
|
||||
Use this helpful Bash script when stargazing
|
||||
======
|
||||
Keep your eyes on the stars by putting your Linux machine in night
|
||||
vision mode with xcalib.
|
||||
![Computer laptop in space][1]
|
||||
|
||||
We often talk about [Linux][2] being used on servers and by developers, but it is used in many other fields too, including astronomy. There are a lot of astronomy tools available for Linux, such as sky maps, star charts, and interfaces to telescope drive systems for controlling your telescope. But one challenge for astronomers is using a computer while keeping their eyes working in the dark.
|
||||
|
||||
When working out in the field at night, astronomers need to preserve their night vision. It can take up to 30 minutes for the human eye to fully dilate and adjust to low light levels, and doing things like checking a phone or laptop at the regular color and brightness levels can cause the eyes to lose their adjustment. This reduces the ability to see in the dark. An example anyone can understand: if you're reading something on your phone in bed at night and get up to go to the bathroom, you know how difficult it can be to see any obstacles that might be in your way.
|
||||
|
||||
### A solution
|
||||
|
||||
I'd like to present a nifty little script to help the astronomer in your family keep "their eyes" in the dark. It relies on a utility called [xcalib][3], a "tiny monitor calibration loader for X.org." It can be installed easily using your Linux package manager.
|
||||
|
||||
On Fedora, for example:
|
||||
|
||||
|
||||
```
|
||||
$ sudo dnf info xcalib
|
||||
$ sudo dnf install xcalib
|
||||
```
|
||||
|
||||
Or Ubuntu:
|
||||
|
||||
|
||||
```
|
||||
`$ sudo apt-get install xcalib`
|
||||
```
|
||||
|
||||
The xcalib application works only with X11, so it is not functional on Wayland systems. But Wayland has this functionality built-in, so you can get the same results through GNOME Settings. If you're using X11, xcalib is an easy way to change the color temperature of your display.
|
||||
|
||||
### The script
|
||||
|
||||
I discovered [Redscreen][4], a night vision filter script written by Jeff Jahr in 2014. The original script is written for the C shell, but Bash is the common default these days. In fact, the C shell is not installed by default on my current Fedora Linux workstation. So, I decided to write an updated version of the Redscreen script aimed at the newest Bash syntax, but I made one major change: utilizing a case statement.
|
||||
|
||||
|
||||
```
|
||||
#!/usr/bin/bash
|
||||
# redscreen.sh Fri Feb 28 11:36 EST 2020 Alan Formy-Duval
|
||||
# Turn screen red - Useful to Astronomers
|
||||
# Inspired by redscreen.csh created by Jeff Jahr 2014
|
||||
# (<http://www.jeffrika.com/\~malakai/redscreen/index.html>)
|
||||
|
||||
# This program is free software: you can redistribute it
|
||||
# and/or modify it under the terms of the GNU General
|
||||
# Public License as published by the Free Software Foundation,
|
||||
# either version 3 of the License, or (at your option) any
|
||||
# later version.
|
||||
|
||||
# This program is distributed in the hope that it will be
|
||||
# useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
# PURPOSE. See the GNU General Public License for
|
||||
# more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public
|
||||
# License along with this program.
|
||||
# If not, see <[http://www.gnu.org/licenses/\>][5].
|
||||
|
||||
case $1 in
|
||||
on)
|
||||
# adjust color, gamma, brightness, contrast
|
||||
xcalib -green .1 0 1 -blue .1 0 1 -red 0.5 1 40 -alter
|
||||
exit 1
|
||||
;;
|
||||
off)
|
||||
xcalib -clear
|
||||
exit 1
|
||||
;;
|
||||
inv)
|
||||
# Invert screen
|
||||
xcalib -i -a
|
||||
exit 1
|
||||
;;
|
||||
dim)
|
||||
# Make the screen darker
|
||||
xcalib -clear
|
||||
xcalib -co 30 -alter
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
echo "$0 [on | dim | inv | off]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
![Skychart for Linux Version 4.2.1 on Fedora workstation][6]
|
||||
|
||||
A lot of astronomy programs include a "night-mode" function, but not all do. Also, this script provides a way to affect the entire screen, not just a specific application. This allows you to use your Linux system out in the field at night for other things than just stargazing—such as checking email or reading Opensource.com—without ruining your night vision.
|
||||
|
||||
Whether you are an astronomer or just an amateur stargazer, you can spend all night admiring the heavens using Linux and open source!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/4/linux-astronomy
|
||||
|
||||
作者:[Alan Formy-Duval][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/alanfdoss
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/computer_space_graphic_cosmic.png?itok=wu493YbB (Computer laptop in space)
|
||||
[2]: https://opensource.com/resources/linux
|
||||
[3]: http://xcalib.sourceforge.net/
|
||||
[4]: http://www.jeffrika.com/~malakai/redscreen/index.html
|
||||
[5]: http://www.gnu.org/licenses/\>
|
||||
[6]: https://opensource.com/sites/default/files/uploads/starchart_in_red.png (A star chart displayed in red screen mode)
|
||||
@@ -1,139 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How young people can help fight COVID-19 with code)
|
||||
[#]: via: (https://opensource.com/article/20/4/covid19-hackathon)
|
||||
[#]: author: (Melissa Sasi https://opensource.com/users/mesassi)
|
||||
|
||||
How young people can help fight COVID-19 with code
|
||||
======
|
||||
Youth developers are invited to submit ideas by April 15 to counter the
|
||||
educational, informational, social, and health challenges uncovered by
|
||||
the COVID-19 pandemic.
|
||||
![woman on laptop sitting at the window][1]
|
||||
|
||||
More than 91% of students around the world are impacted by school closures due to COVID-19, and most governments have temporarily closed academic institutions. That's nearly [1.6 billion young people in 188 countries][2]. Also, most of the learning platforms available online today aren't practical, engaging, or interactive, and lack true virtual collaboration.
|
||||
|
||||
This big and wicked challenge got me thinking about how these circumstances are impacting my children, their friends, and my passions of empowering youth, fostering tech entrepreneurship, and inspiring under-represented communities to find their purpose through building digital skills. These all came together in [CodeTheCurve][3], "a global, virtual hackathon for students, educators, teachers, and the research community to build tech skills, entrepreneurial spirit, and professional competencies to build digital creativity and cooperation to mobilize the world."
|
||||
|
||||
We hope you'll want to participate, but you need to act fast: the deadline to submit proposals is April 15.
|
||||
|
||||
### My story
|
||||
|
||||
The passions mentioned above stem from a deeply personal journey: My children and I are victims of parental kidnapping, and access to the internet and digital literacy are my pathways to being a mother from afar. My children, Zahra (age 13), Zahran (15), and Youmna (18), are safe and healthy, and we are frequently connected. They're living the same life youth all over the world are living these days, trying to social distance and remain in good health while figuring out this school thing (or lack thereof)—only one of my children has access to formal virtual learning during to COVID-19 school closures. The other two, without school-driven online learning options, tend to stay up all night playing Fortnight and making TikTok videos.
|
||||
|
||||
I have always been passionate about digital inclusion and empowering the world through computer science, and the effects of COVID-19 have increased my desire to make a difference. About four years ago, I created a non-profit organization, [MentorNations][4], to inspire youth and the world via technology. My non-profit has taught tens of thousands of young people in 12 countries to code. In my work at IBM as a developer advocate, I focus on empowering early-stage entrepreneurs, developers, and students with access to tech skills, professional development, and entrepreneurial thinking. My major focus areas include inspiring students to discover their career potential in enterprise computing while recognizing that we are all ANDs and not ORs.
|
||||
|
||||
Teaching the next generation about the power of collaboration, teamwork, problem-solving, and critical thinking that happen through open source code and principles empowers them to be creators and innovators who focus on solving relevant and real-world problems.
|
||||
|
||||
Looking through the lens of my children, my non-profit work, my roles within a variety of United Nations Task Forces, and my position as IEEE Chair over the Digital Skills Working Group, I wondered, _**what can I do to make a difference with open source technology?**_ So I reached out to my network, and the world responded in a much bigger way than I had ever imagined.
|
||||
|
||||
### CodeTheCurve
|
||||
|
||||
In response, we launched UNESCO's [CodeTheCurve][3] hackathon in collaboration with 14 partners, including UN EQUALS, SAP, iHackOnline, Angel Hack, Internet Society, and YPO. Participants are invited to bring their open source ideas to combat the current and future environment and challenges relating to COVID-19. This initiative is centered around youth empowerment, gender inclusion, and making the world a better place for our communities, including for people we may not directly encounter daily.
|
||||
|
||||
CodeTheCurve is for anyone above the age of 16. To ensure gender, age, and experience diversity, teams must include a developer or data scientist (early-stage chops are fine); at least one person under the age of 25; and at least one male and one female. The 40 teams selected to participate in CodeTheCurve will have access to more than 80 business and technical mentors (experts!) from around the world to help turn their ideas into reality.
|
||||
|
||||
My vision for this hackathon is to train young talent; enable them with free, online resources and access to real people with real answers; and encourage the creation of real-world problem solving in real-time. The results of the hackathon, I hope, will be open source utilities and information that can be used, in some way, to combat COVID-19.
|
||||
|
||||
#### Week-long learning, bootcamp, and hacking experience
|
||||
|
||||
CodeTheCurve is a three-day virtual hackathon experience guided by expert business and technical mentors. Before the hackathon proper, participants begin with two days of self-paced, online learning from content curated by CodeTheCurve collaborators, followed by a two-day, instructor-led learning journey where the 40 selected teams will collaborate in virtual breakout rooms with activity kits, hands-on computing resources in machine learning, and expert-guided plenary sessions.
|
||||
|
||||
#### CodeTheCurve hackathon themes
|
||||
|
||||
CodeTheCurve includes three themes:
|
||||
|
||||
* Education
|
||||
* Information and data management
|
||||
* Current and post-COVID-19 health and social issues
|
||||
|
||||
|
||||
|
||||
#### Professional development, entrepreneurship, and hands-on open source skills
|
||||
|
||||
The 40 teams will be empowered with expert-guided, engaging activities, including the following skill-building opportunities:
|
||||
|
||||
* **Hand-on tech skills**
|
||||
* Using Jupyter Notebooks for data science
|
||||
* Data protection, privacy, security, and encryption
|
||||
* Machine learning and artificial intelligence
|
||||
* Architectural diagrams and frameworks
|
||||
* Technical roadmaps
|
||||
* **Professional development**
|
||||
* Design thinking
|
||||
* Personal branding
|
||||
* Communication skills
|
||||
* How not to feel like an imposter
|
||||
* Conflict resolution
|
||||
* Working in global, virtual teams
|
||||
* Media literacy
|
||||
* Ethics in machine learning and artificial intelligence
|
||||
* **Entrepreneurship**
|
||||
* Problem statements
|
||||
* Mission and vision statements
|
||||
* Value propositions
|
||||
* Audience and target markets
|
||||
* Business model canvassing
|
||||
* Pitch decks
|
||||
* Pitch practice
|
||||
|
||||
|
||||
|
||||
### April 15: CodeTheCurve deadline
|
||||
|
||||
Did I mention that the initial application deadline is April 15? Here's the full timeline:
|
||||
|
||||
* Video submission deadline: **April 15**
|
||||
* 40 selected teams announced: **April 20**
|
||||
* Learning resources for pre-collaboration: **April 20-21**
|
||||
* Instructor-led learning: **April 22-23**
|
||||
* Hacking: **April 24-26**
|
||||
* CodeTheCurve winners announced: **April 30**
|
||||
|
||||
|
||||
|
||||
#### Prizes. Prizes. Prizes.
|
||||
|
||||
Prizes include free access to [IBM LinuxONE Community Cloud][5] for one year, free training courses from SAP, four pitch opportunities at IBM and SAP events, free access to enterprise-grade IBM Z and its machine learning suite for six months, and one-on-one technical and business mentorship for a full year with industry experts.
|
||||
|
||||
### How to apply
|
||||
|
||||
Interested in applying? Know someone who should apply? Simply [submit a video][6] of your **amazing** open source idea, the problem you're trying to solve, and who you expect to reach.
|
||||
|
||||
If you'd like to learn more, here are some other articles about CodeTheCurve:
|
||||
|
||||
* [UN News CodeTheCurve article][7]
|
||||
* [UNESCO CodeTheCurve blog][8]
|
||||
* [Forbes CodeTheCurve article][9]
|
||||
* [IBM CodeTheCurve blog][10]
|
||||
|
||||
|
||||
|
||||
I cannot wait to see all the amazing open source ideas the world brings our way!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/4/covid19-hackathon
|
||||
|
||||
作者:[Melissa Sasi][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/mesassi
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/lenovo-thinkpad-laptop-window-focus.png?itok=g0xPm2kD (young woman working on a laptop)
|
||||
[2]: https://en.unesco.org/covid19/educationresponse
|
||||
[3]: https://www.codethecurve.org/
|
||||
[4]: https://mentornations.org/
|
||||
[5]: https://developer.ibm.com/linuxone/
|
||||
[6]: http://ibm.biz/codethecurve-apply
|
||||
[7]: https://news.un.org/en/story/2020/04/1061142
|
||||
[8]: http://ibm.biz/unesco-pr
|
||||
[9]: https://www.forbes.com/sites/danielnewman/2020/04/10/digital-transformation-for-good-shines-as-we-fight-covid-19/#78d51a4c4946
|
||||
[10]: http://ibm.biz/codethecurve
|
||||
@@ -1,196 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Try this Kubernetes HTTP router and reverse proxy)
|
||||
[#]: via: (https://opensource.com/article/20/4/http-kubernetes-skipper)
|
||||
[#]: author: (Sandor Szücs https://opensource.com/users/sszuecs)
|
||||
|
||||
Try this Kubernetes HTTP router and reverse proxy
|
||||
======
|
||||
Skipper is designed to handle large numbers of HTTP route definitions,
|
||||
beyond what you would want to manage in Nginx or Apache.
|
||||
![Traffic circle with arrows pointing which way to go][1]
|
||||
|
||||
Skipper is an open source HTTP router and reverse proxy for service composition. As its [GitHub page][2] states, it's designed to handle large amounts of dynamically configured HTTP route definitions (>600,000 routes) with detailed lookup conditions and flexible augmentation of the request flow with filters. It can be used out of the box or extended with custom lookup, filter logic, and configuration sources.
|
||||
|
||||
### Proxies
|
||||
|
||||
When some people think of a proxy, they imagine a webpage that serves as a gateway to an intranet or a suspicious-looking webpage designed to unblock social media sites on a school or work network. A forward proxy is one that operators of desktop infrastructure use to save internet bandwidth, enforce parental controls, or limit social media access. Another kind of proxy is one in which an individual user navigates to a page, provides credentials, and is then forwarded to a protected intranet resource. The inverse of that kind of proxy is the reverse proxy, which accepts all traffic and forwards it to a specific resource, like a server or container. That's the kind of work Skipper does for infrastructure.
|
||||
|
||||
When I read [Matt Klein's post][3] on modern network load balancing and proxying, I realized that we, as [Skipper][4] maintainers, should explain more features and details about why and how you can leverage HTTP proxies. In this article, I will treat the terminology "HTTP (reverse) proxy" and "HTTP router" as the same.
|
||||
|
||||
### HTTP routing
|
||||
|
||||
According to [Wikipedia][5]: "Routing is the process of selecting a path for traffic in a network." This definition refers to routing at [OSI layer 3][6], most commonly based on [IP][7] with routing protocols like [BGP][8] or [OSPF][9]. Since this article isn't about one of those, I will try to explain what HTTP routers are about. But first, I want to introduce Skipper, an [OSI layer 7][10] HTTP router library written in [Go][11] and a core component of retailer [Zalando][12]'s e-commerce shop and the [Kubernetes][13] Ingress infrastructure.
|
||||
|
||||
At Zalando, we use Skipper as a [Kubernetes Ingress][14] controller to support our users with visibility, reliability, security, and additional features to offload common applications.
|
||||
|
||||
Any organization running HTTP services, often in a microservice architecture, needs to route HTTP requests to the right applications. HTTP routers route based on information provided by the HTTP request. For example, the following shows an HTTP/1.1 request.
|
||||
|
||||
|
||||
```
|
||||
GET /details HTTP/1.1
|
||||
Host: [www.zalando.de][15]
|
||||
User-Agent: curl/7.49.0
|
||||
Accept: */*
|
||||
Authorization: Bearer <token>
|
||||
...
|
||||
```
|
||||
|
||||
We can route based on the method **GET**, the path **/details**, the **Host** header [**www.zalando.de**][16], or any arbitrary part of the request.
|
||||
|
||||
One common problem an application owner faces is splitting an API into multiple applications, so you need to split the responsibility of a component into subcomponents. Another common task is to support refactoring; maybe you have rewritten one part of your app, and you want to deploy it separately now.
|
||||
|
||||
For example, imagine you you have a store that has a list of products and their details, and you need to split it into _shop_ and _product_ backend applications. At **/**, your shop shows the list of products, and at **/details**, it shows product details, such as color, size, sustainability, and price.
|
||||
|
||||
![Figure 1: shop][17]
|
||||
|
||||
You need to split the responsibility of the product detail into its own application, such that **/** stays in the _shop_ application and **/details** is refactored to the _product_ application.
|
||||
|
||||
![Figure 2: product and shop][18]
|
||||
|
||||
To make sure an HTTP proxy finds the right backend for an incoming request, it uses a routing table to check the destination to make sure it's correct.
|
||||
|
||||
### Routing tables
|
||||
|
||||
In Skipper, the routing table is created by pulling information generated by [dataclients][19] from different sources. One source can be a [routes file][20], similar to what you may see in more popular HTTP servers, like Apache or Nginx.
|
||||
|
||||
Depending on the size of your organization—or better, the number of backend applications—the routing table can grow quite large. Skipper implements the routing table as a tree that can scale beyond 600,000 routes (far more than you'd want to manage in an Nginx or Apache config).
|
||||
|
||||
Following along with the example application above, Table 1 shows the routing table from [Figure 2][21]. The store **/** should be routed to **shop,** and the **/detail** routed to the **product** application.
|
||||
|
||||
path | app
|
||||
---|---
|
||||
/ | shop
|
||||
/detail | product
|
||||
|
||||
Table 1: Routing table
|
||||
|
||||
The available dataclients in Skipper fetch routes from different sources and what a route consists of.
|
||||
|
||||
### Dataclient
|
||||
|
||||
The routing configuration in Skipper's routes file [dataclient][22] is similar to what you might know from HTTP proxies in Nginx or Apache. In Skipper, a routes file specifies all routes in [eskip][23] syntax, as shown in Figure 3.
|
||||
|
||||
|
||||
```
|
||||
r1: P1() && P2() && .. && PN()
|
||||
-> f1()
|
||||
-> f2()
|
||||
...
|
||||
-> fN()
|
||||
-> <backend>;
|
||||
r2: ...
|
||||
...
|
||||
```
|
||||
|
||||
Figure 3: Routes file in eskip
|
||||
|
||||
In the above:
|
||||
|
||||
* **r1, r2, ...** are unique routeIDs.
|
||||
* **P1, P2,..,PN** are predicates that define the matching.
|
||||
* **f1, f2,..,fN** are filters that are applied after the route was selected. Filters can change the request and response.
|
||||
* Finally, the Skipper backend is defined. This can be a single URL, a list of load-balanced URLs, and others for special cases such as [direct response][24].
|
||||
|
||||
|
||||
|
||||
The [routes string][25] is another dataclient that is handy for tests. For example, if you need a pseudo backend for your demo that replies a green background with HTML, you could use:
|
||||
|
||||
|
||||
```
|
||||
$ skipper -routes-string='*
|
||||
-> inlineContent(
|
||||
"<html><body style=\"background-color: green;\"></body></html>"
|
||||
)'
|
||||
```
|
||||
|
||||
Skipper's most popular dataclient, by far, is the Kubernetes dataclient, which is used to fetch information from a [Kubernetes API server][26] and create a routing table from [Skipper Ingress][27] resources and the [RouteGroup][28] custom resource definition (CRD).
|
||||
|
||||
To summarize the above, dataclients fetch information from different providers to build Skipper's routing table. Table 1 shows a routing table for the shop/description example, and Skipper uses predicates to select the route to process the request.
|
||||
|
||||
### Predicates
|
||||
|
||||
In Skipper, an incoming request is matched to [predicates][29] of all the routes to find the best matching route for an incoming request. Predicates are functions that match based on the incoming request. In the example from Figure 2 and Table 1, Skipper would have a routing table similar to Figure 4:
|
||||
|
||||
|
||||
```
|
||||
shop: Path("/")
|
||||
-> "<https://shop.zalando>";
|
||||
product: Path("/detail")
|
||||
-> "<https://product.zalando>";
|
||||
```
|
||||
|
||||
Figure 4: Skipper routing table
|
||||
|
||||
This means HTTP requests with a path **/** would be matched by the **Path("/")** predicate, such that Skipper will execute the shop route. Requests with a path **/detail** would be matched by **Path("/detail")** and routed to the product application.
|
||||
|
||||
In general, routing behavior can be changed by predicates. There are a lot of predicates you can choose from. For example, **Method("POST")** will be true only if a POST request would be passed. A route with more predicates is considered more specific. Also, a route with more predicates has more weight in the route selection than one that has less.
|
||||
|
||||
Special cases are **Path()** and **PathSubtree()**, which is matched first in a tree and reduces the number of routes, which are scanned as a list. For example, the tree structure shown in Figure 5 helps to scale the number of routes to more than 600,000 in one of Zalando's production setups.
|
||||
|
||||
![Skipper tree example][30]
|
||||
|
||||
### Filters
|
||||
|
||||
After a route is selected, the request [filters][31] are applied. Filters work on request or response; they can change the incoming request to the backend, and they can change the response to the client.
|
||||
|
||||
For example, **setRequestHeader("Foo", "bar")** sets the HTTP header **"Foo"** to the value **"bar"**, such that the backend sees this header in the request.
|
||||
|
||||
The response filter **responseCookie("keks", "val", 3600)** sets a Cookie named **"keks"** in the response to the caller, which might be a browser in this case. The cookie would have the value **"val"** and is valid for one hour.
|
||||
|
||||
One filter that works on request and response is **enableAccessLog(40, 5)**. This would do access logs for all responses from the backend with status codes 40x or 5xx.
|
||||
|
||||
As you can see from the examples, filters can change the request or the response or just do some work based on it. Another filter example is **auth filters** or **ratelimits**. These would stop requests from passing to the backend if the request should not be allowed to pass. For example, to serve static content from a directory called **/var/www**, you can use the filter **static("/var/www")**.
|
||||
|
||||
### Learn more
|
||||
|
||||
This article provided a basic overview of Skipper and its capabilities. For more information, consult [Skipper's documentation][32], and please share your questions or feedback in the comments.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/4/http-kubernetes-skipper
|
||||
|
||||
作者:[Sandor Szücs][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/sszuecs
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/LAW-patent_reform_520x292_10136657_1012_dc.png?itok=Cd2PmDWf (Traffic circle with arrows pointing which way to go)
|
||||
[2]: https://github.com/zalando/skipper
|
||||
[3]: https://blog.envoyproxy.io/introduction-to-modern-network-load-balancing-and-proxying-a57f6ff80236
|
||||
[4]: https://opensource.zalando.com/skipper
|
||||
[5]: https://en.wikipedia.org/wiki/Routing
|
||||
[6]: https://en.wikipedia.org/wiki/OSI_model#Layer_3:_Network_Layer
|
||||
[7]: https://en.wikipedia.org/wiki/Internet_Protocol
|
||||
[8]: https://en.wikipedia.org/wiki/Border_Gateway_Protocol
|
||||
[9]: https://en.wikipedia.org/wiki/Open_Shortest_Path_First
|
||||
[10]: https://en.wikipedia.org/wiki/OSI_model#Layer_7:_Application_Layer
|
||||
[11]: https://golang.org/
|
||||
[12]: https://en.zalando.de/
|
||||
[13]: https://kubernetes.io
|
||||
[14]: https://kubernetes.io/docs/concepts/services-networking/ingress/
|
||||
[15]: http://www.zalando.de
|
||||
[16]: https://en.zalando.de/?_rfl=de
|
||||
[17]: https://opensource.com/sites/default/files/uploads/skipper_1_shop.png (Figure 1: shop)
|
||||
[18]: https://opensource.com/sites/default/files/uploads/skipper_2_product-shop.png (Figure 2: product and shop)
|
||||
[19]: https://opensource.zalando.com/skipper/reference/backends/
|
||||
[20]: https://opensource.zalando.com/skipper/data-clients/eskip-file/
|
||||
[21]: tmp.ftM58r5YpM#fig2
|
||||
[22]: https://opensource.zalando.com/skipper/tutorials/development/#dataclients
|
||||
[23]: https://godoc.org/github.com/zalando/skipper/eskip
|
||||
[24]: https://opensource.zalando.com/skipper/reference/backends/#shunt-backend
|
||||
[25]: https://opensource.zalando.com/skipper/data-clients/route-string/
|
||||
[26]: https://kubernetes.io/docs/concepts/overview/components/#kube-apiserver
|
||||
[27]: https://opensource.zalando.com/skipper/kubernetes/ingress-usage/
|
||||
[28]: https://opensource.zalando.com/skipper/kubernetes/routegroups/
|
||||
[29]: https://opensource.zalando.com/skipper/reference/predicates/
|
||||
[30]: https://opensource.com/sites/default/files/uploads/skipper_5_tree.png (Skipper tree example)
|
||||
[31]: https://opensource.zalando.com/skipper/reference/filters/
|
||||
[32]: https://opensource.zalando.com/skipper/
|
||||
@@ -1,174 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Is reporting 100% of code coverage reasonable?)
|
||||
[#]: via: (https://opensource.com/article/20/4/testing-code-coverage)
|
||||
[#]: author: (Eric Herman https://opensource.com/users/ericherman)
|
||||
|
||||
Is reporting 100% of code coverage reasonable?
|
||||
======
|
||||
The time required to reach reporting 100% of code coverage is
|
||||
considerably less than what I would have estimated before this
|
||||
exploration.
|
||||
![Code going into a computer.][1]
|
||||
|
||||
The [Foundation for Public Code][2] works to enable open and collaborative public-purpose software for public organizations (like local governments) internationally. We do this by supporting software at the codebase level through codebase stewardship. We also publish the [Standard for Public Code][3] (draft version 0.1.4 at the time of this writing), which helps open source codebase communities build solutions that can be reused successfully by other organizations. It includes guidance for policymakers, managers, developers, designers, and vendors.
|
||||
|
||||
Among other things, the standard addresses [code coverage][4], or how much of the code is executed when an automated test suite runs. It's one way to measure the likelihood that the code contains undetected software bugs. In the standard's ["Use continuous integration" requirements][5], it says, "source code test and documentation coverage **should** be monitored." Additionally, the [guidance to check][6] this requirement states, "code coverage tools check whether coverage is at 100% of the code."
|
||||
|
||||
Over my software development career, which spans more than two decades, I have worked on codebases large and small and some with very high percentages of code coverage. Yet none of the non-trivial codebases I have contributed to have reported 100% test coverage. This made me question whether the "_check whether coverage is at 100%_" guidance would be followed.
|
||||
|
||||
When I think about the nature of the test coverage gaps in the codebases I have worked on, they typically have been around system states that are very difficult (and in some cases, impossible) to create. For instance, in earlier versions of Java, I recall we were required to write catch blocks for exceptions that could never be thrown.
|
||||
|
||||
Previously, I reasoned that 100% test coverage is something to aspire to, but it is probably not worth the cost on most codebases and may not be realistic in a few.
|
||||
|
||||
Coverage tools have been getting smarter and more tunable over time. Languages have been getting lighter, and libraries have been getting easier to mock and test. So how unreasonable is 100% coverage of functionality today?
|
||||
|
||||
### Resource exhaustion
|
||||
|
||||
The high-quality but low test-coverage codebases I contribute to happen to be written in C or C++. A quick glance at these codebases shows that there is a class of common low-coverage situations that I'll lump together under the umbrella of resource exhaustion: out of memory, out of disk space, etc.
|
||||
|
||||
Here is a simple example of code that does not check for resource exhaustion; in this case, memory allocation failure:
|
||||
|
||||
|
||||
```
|
||||
char *buf = malloc(80);
|
||||
sprintf(buf, "hello, world");
|
||||
```
|
||||
|
||||
This example code needs to allocate a small buffer, so it calls **malloc(80)**, and **malloc** usually returns a pointer to 80 bytes of memory … but that can fail. In the (unlikely) case that **malloc** returns **NULL**, the code above will proceed to call **sprintf** with a **NULL** pointer which causes a crash. It is typical in C code to do something more like this:
|
||||
|
||||
|
||||
```
|
||||
char *buf = malloc(80);
|
||||
if (buf == NULL) {
|
||||
fprintf(stderr, "malloc returned NULL for 80 bytes?\n");
|
||||
return NULL;
|
||||
}
|
||||
sprintf(buf, "hello, world");
|
||||
```
|
||||
|
||||
This code guards against **malloc** returning **NULL**, which is better. However, creating tests for correct behavior in the face of this kind of resource exhaustion can be really hard. It's not impossible, of course, and there are multiple approaches. Many approaches result in fragile tests, which require a lot of maintenance over time, and these tests can be very time-consuming to build in the first place.
|
||||
|
||||
### Exploration
|
||||
|
||||
Pondering this, I decided to run a little experiment to see if I could learn something about the costs and consequences of this strict, 100% criterion.
|
||||
|
||||
Since I do some embedded-systems development, I have a few C libraries that I've developed and reused over the years in my embedded projects. I decided to look at some of these libraries and see just how hard it would be to bring them up to 100% code coverage. In the process, I paid attention to the impact on code clarity, code structure, and performance.
|
||||
|
||||
#### A library with preexisting dependency injection
|
||||
|
||||
Step one is measuring by adding code coverage to a codebase. Since this is C, **gcc** provides quite a lot by default with the **\--coverage** option, and **lcov** (with **genhtml**) does a good job of making reports; thus, this step was easy. I expected the starting coverage to be pretty good—it was, but it had a few untested branches, as well as the predicted gaps around error conditions and error reporting.
|
||||
|
||||
I made error reporting pluggable, so it was easier to capture and make assertions around error messages in previously untested branches.
|
||||
|
||||
Since this code already allowed for pluggable implementations of **malloc** and **free**, it was straightforward to write little malloc and free wrappers that I could inject memory allocation failures into. Within an hour or two, that was covered.
|
||||
|
||||
In the process, I realized that there was one condition where, from the perspective of the calling client code, it was impossible to distinguish between the situation where an error occurs and one where **NULL** is a valid return value. For you C programmers, it was essentially similar to the following:
|
||||
|
||||
|
||||
```
|
||||
/* stashes a copy of the value
|
||||
* returns the previously stashed value */
|
||||
char *foo_stash(foo_s *context,
|
||||
char *stash_me,
|
||||
size_t stash_me_len)
|
||||
{
|
||||
char *copy = malloc(stash_me_len);
|
||||
if (copy == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(copy, stash_me, stash_me_len);
|
||||
char *previous = context->stash;
|
||||
context->stash = copy;
|
||||
/* previous may be NULL */
|
||||
return previous;
|
||||
}
|
||||
```
|
||||
|
||||
I adjusted the API to allow the error information to be explicitly available. If you are a C developer, you know there are various ways this can be accomplished. I chose an approach similar to this:
|
||||
|
||||
|
||||
```
|
||||
/* stashes a copy of the value
|
||||
* returns the previously stashed value
|
||||
* on error, the 'err' pointer is set to 1 */
|
||||
char *foo_stash2(foo_s *context,
|
||||
char *stash_me,
|
||||
size_t stash_me_len,
|
||||
int *err)
|
||||
{
|
||||
char *copy = malloc(stash_me_len);
|
||||
if (copy == NULL) {
|
||||
*err = 1;
|
||||
return NULL;
|
||||
}
|
||||
memcpy(copy, stash_me, stash_me_len);
|
||||
char *previous = context->stash;
|
||||
context->stash = copy;
|
||||
/* previous may be NULL */
|
||||
return previous;
|
||||
}
|
||||
```
|
||||
|
||||
Without testing for resource exhaustion, it may have taken a long time for me to notice this (now obvious) shortcoming of the API.
|
||||
|
||||
To get **lcov** to report 100% test coverage, I had to tell the compiler to [not inline any code][7], something I learned it does even at optimization level zero.
|
||||
|
||||
When embedded in actual firmware, the compiler optimized away the unused indirection; therefore, the added indirection in the source code imposed no real-world performance penalty in the compiled firmware.
|
||||
|
||||
Of course, this was the easy library.
|
||||
|
||||
#### A more typical library
|
||||
|
||||
Once I established a method of injecting memory allocation failures in tests, I decided to move onto another library, but one for which malloc and free were not already pluggable. I had questions. How invasive will this be to the codebase? Will it clutter the code, making it less clear? How time-consuming will it be?
|
||||
|
||||
While I don't always record coverage metrics, I am a big believer in testing: more than 20 years ago, I learned that my code improves if I write the tests and client code [before][8] the implementation code, and I have worked that way ever since. (In [_Test-Driven Development: By Example_][9], you can find my name in the acknowledgments.) Yet, when I added code coverage reporting to the second library, I was surprised to see that (at some point in the past) I had added a pair of functions to the library without adding tests for them. The other untested areas were, unsurprisingly, code to handle memory-allocation failure.
|
||||
|
||||
Writing tests for the pair of untested functions was, of course, quick and easy. The coverage tools also revealed that I had a function with an untested code branch that, given only a quick glance, contained a bug. The fix was trivial, yet I was surprised to find a bug, given the different projects where I use this library. Nonetheless, there it was, a humbling reminder that, all too often, bugs lurk in untested code.
|
||||
|
||||
Next up was the more challenging stuff: testing for resource exhaustion. I started by introducing some global variables for the malloc/free function pointers, as well as a variable to hold a memory-tracking object. Once that was working, I moved those variables from global scope into a context argument that was already present. Refactoring the code to allow for the necessary indirection took only a couple of hours (less time than I expected), and the complexity added was negligible.
|
||||
|
||||
### Reflections
|
||||
|
||||
My conclusion from the first library was that it was well worth the time. The code is now more flexible, the API is now more complete for the caller, and writing the failure injection harness was pretty easy.
|
||||
|
||||
From the second library, I was reminded that even less-pluggable code could be made testable without adding undue levels of complexity. The code improved, I fixed a bug, and I can be more confident in the code. Also, the additional modularity of being able to plug in an alternative memory allocator is a feature that may prove more valuable in the future.
|
||||
|
||||
Exclusion comments are a feature of **lcov** to cause coverage reporting to ignore a block of code. Interestingly, I didn't feel the need to use exclusion comments in either library.
|
||||
|
||||
I am more certain than ever that even very good code is improved by investing in test coverage.
|
||||
|
||||
Both of these codebases are small, had some modularity already, began from a point of good testing, are single-threaded, and contain no graphical UI code. If I were to try to tackle this on one of the larger, more monolithic codebases I contribute to, it would be harder and require a larger time investment. There would likely be some sections of code where I might still conclude that the best thing to do would be to "cheat" by tuning the tooling to not report on some section of code.
|
||||
|
||||
That said, I estimate that the time required to reach reporting 100% of code coverage is considerably less than what I would have estimated before this exploration.
|
||||
|
||||
If you happen to be a C coder and want to see a running example of this, including **gcov** / **lcov** usage, I extracted the out-of-memory injecting code and put it in an [example repository][10].
|
||||
|
||||
Have you pushed a codebase to 100% coverage by tests, or tried to? What was your experience? Please share it in the comments.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/4/testing-code-coverage
|
||||
|
||||
作者:[Eric Herman][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/ericherman
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/code_computer_development_programming.png?itok=4OM29-82 (Code going into a computer.)
|
||||
[2]: https://publiccode.net/
|
||||
[3]: https://standard.publiccode.net
|
||||
[4]: https://en.wikipedia.org/wiki/Code_coverage
|
||||
[5]: https://standard.publiccode.net/criteria/continuous-integration.html#requirements
|
||||
[6]: https://standard.publiccode.net/criteria/continuous-integration.html#how-to-test
|
||||
[7]: https://twitter.com/Eric_Herman/status/1224983465784938496
|
||||
[8]: https://opensource.com/article/20/2/automate-unit-tests
|
||||
[9]: https://www.oreilly.com/library/view/test-driven-development/0321146530/
|
||||
[10]: https://github.com/ericherman/context-alloc
|
||||
@@ -1,116 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (A stress-free guide to keeping WordPress sites updated)
|
||||
[#]: via: (https://opensource.com/article/20/4/updating-wordpress)
|
||||
[#]: author: (Sara Kelly https://opensource.com/users/sarapk)
|
||||
|
||||
A stress-free guide to keeping WordPress sites updated
|
||||
======
|
||||
This practical guide to a necessary task will show you how to maximize
|
||||
site performance and avoid bugs and other issues with regular updates.
|
||||
![Working from home at a laptop][1]
|
||||
|
||||
We all know how important it is to keep WordPress sites updated. New updates provide the latest bug and security fixes against any nasties lurking on the web. But, more critically, an outdated site can also lead to poor performance, such as slow loading speed or an outdated look and feel.
|
||||
|
||||
Unfortunately, keeping your WordPress site up-to-date is not as easy as clicking a button. There are several components to consider, from theme to plugins to PHP. Even worse, updating too quickly can wreak another kind of havoc. Have you ever experienced the dreaded, "There has been a critical error on your website" warning after an innocent little update? I know I have, many times!
|
||||
|
||||
Here is a practical guide on what to look out for, as well as when and what to update, to ensure your WordPress site works well.
|
||||
|
||||
### Updating WordPress
|
||||
|
||||
Let's start with the basics. Check your WordPress version is up-to-date by visiting Dashboard > Updates.
|
||||
|
||||
![WordPress update screen][2]
|
||||
|
||||
### Choosing a WordPress theme
|
||||
|
||||
Before we deep dive into updating themes, I'd like to take a few steps back. Choose an up-to-date theme from the get-go and do your homework before installing it! There is nothing worse than pouring your heart and soul into customizing a new theme, only to discover it is buggy.
|
||||
|
||||
Questions to ask when choosing a theme include:
|
||||
|
||||
* When was it first created?
|
||||
* What is the current version available?
|
||||
* Does the theme provider still maintain an active demo site and helpdesk?
|
||||
* What do recent reviews say about the theme?
|
||||
|
||||
|
||||
|
||||
If the theme provider is no longer maintaining the theme, save yourself the trouble and move on. Also, don't assume that just because you paid for a theme, that is necessarily maintained. I recently fell into this trap when I purchased [Pinable][3]. I loved the Pinterest look and feel. However, soon after installation, I noticed the lack of customization within the theme settings, major compatibility issues arose with my plugins, and the customer service was nonexistent. I should have known better. The theme was created in 2013 and selling for a bargain.
|
||||
|
||||
If you already have a theme, then pay attention to how frequently updates become available. If there are never any updates, the theme provider may have closed up shop. It is only a matter of time before the impact of an outdated theme will cause problems.
|
||||
|
||||
A quick aside while we are on the topic—up-to-date themes also give access to the two new alignment options in the WordPress block editor, which enable wide-width and full-width images. These help your blog posts look more professional. While there are a number of [tutorials][4] on the web that show you how to manually update your functions, PHP file, and CSS to enable the new alignment blocks, the code does not always work on older themes (especially masonry themes).
|
||||
|
||||
![Wordpress theme][5]
|
||||
|
||||
### Updating themes
|
||||
|
||||
To check the current version of your theme, go to Appearance > Themes and click on the active theme to see the current version. If an upgrade is available, there will be an alert banner. Click on "update now" to initiate the update. You can also check for updates by going to Dashboard > Updates.
|
||||
|
||||
![Themify screenshot][6]
|
||||
|
||||
If you purchased a theme from a marketplace such as [Envato][7] or [Themify][8], check the theme documentation to learn what is required to initiate updates, as it will not show up automatically in the dashboard. In most cases, you will be required to download and install a specific plugin or manually upload new versions when they become available. In the latter case, you will need to delete or rename the old theme file via your cPanel before you can install the new one. A guide to installing themes via cPanel is available [here][9].
|
||||
|
||||
If you plan to customize your theme extensively and are worried about the impact of this when upgrading, consider creating a child theme first. A child theme lets you make changes without touching the original theme's code. You can then update your site without losing any customizations you've made. Read more about child themes [here][10].
|
||||
|
||||
As I said before, the source of most issues tends to be the theme. Learn what is required to keep your theme up to date, and do so regularly. If your theme provider is no longer creating updates, then find a new theme.
|
||||
|
||||
### Easy does it for plugins
|
||||
|
||||
If you manage multiple plugins, then you will be used to the frequent dashboard reminders to update! Before we get onto that, though, let's touch on some basics.
|
||||
|
||||
As a general rule, you don't want to have too many plugins. They slow down the speed of your site by creating more code that the browser has to load. Always delete any inactive plugins. I prefer to manage plugins on the Plugins tab. Here you can see all active and inactive plugins, the current version, and whether an update is available. To update the plugin, simply click "update."
|
||||
|
||||
![Plugin update page][11]
|
||||
|
||||
Nonetheless, I implore you to wait a week or two before installing new updates. Updating my plugins too quickly has caused me no end of grievances. To begin with, updates are prone to human error. Don't be the guinea pig that tests out the latest version. Sometimes, the newest version of a plugin is not compatible with an older version of WordPress or your theme. Check these are up-to-date first.
|
||||
|
||||
### Website down after updating plugins?
|
||||
|
||||
If your site has stopped working or performance has dropped noticeably after updating your plugins, then all is not lost. Forget about those newfangled plugins that promise to test speed and identify buggy plugins (the last thing you want is more plugins)! Disable all your plugins, then activate one at a time while you test the speed and performance of your site on a website such as [Pingdom][12]. This is a great exercise to perform periodically, even if your website has not crashed. Once you identify the plugin causing the problem, delete it.
|
||||
|
||||
In the event you cannot access WordPress because there is a critical error, then you will need to access your files via cPanel and delete all the plugin folders from there ([full instructions here][13]). Don't worry; doing this will not impact your website's content. You can then proceed to reinstall and activate the plugins one-by-one via WordPress.
|
||||
|
||||
Cache plugins tend to be the biggest culprit in my experience. Issues with cache plugins can be minimized by clearing the cache frequently. Do not install multiple cache plugins that perform the same function, as they will only serve to slow down your site. The only way to truly get around cache plugin issues is to either not use them, use a plugin recommended by your hosting provider, or become an expert on cache. [This blog][14] on common cache issues in WordPress is a good place to start.
|
||||
|
||||
### Back up before updating PHP
|
||||
|
||||
If you are concerned about your website speed and have spent enough time browsing Google for answers, then you likely have seen the advice, "You gotta update your PHP!" Please tread carefully with manual PHP updates, though! If you have a good hosting provider, you should never need to do this. Rather, select the option for automatic PHP version management with your host. Newer versions of PHP may not be stable or compatible with the version of WordPress you are running. Let your hosting provider be the one to determine when updates are ready.
|
||||
|
||||
However, if you are adamant that an old version of PHP is causing your website to be slow, take care to follow these steps before initiating an update. First, back up your site. Investing in a premium version of [Jetpack][15] is worth its weight in gold. Jetpack can perform real-time as well as daily backups, depending on your plan. Not to mention, their customer service and troubleshooting support are excellent. Secondly, inform your hosting provider that you plan to update the PHP and seek their advice first. If your host is unable to advise or wants to charge you for the privilege, you should probably think about changing hosts.
|
||||
|
||||
You can update PHP either via cPanel or via your hosting platform under Devs > PHP Manager. After that, you are on your own, as that is where my expertise on PHP ends.
|
||||
|
||||
If you have any other tips or pitfalls regarding updating WordPress, drop them in the comments box below.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/4/updating-wordpress
|
||||
|
||||
作者:[Sara Kelly][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/sarapk
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/wfh_work_home_laptop_work.png?itok=VFwToeMy (Working from home at a laptop)
|
||||
[2]: https://opensource.com/sites/default/files/uploads/wp_update_1.png (Wordpress update screen)
|
||||
[3]: https://www.theme-junkie.com/themes/pinable/
|
||||
[4]: https://www.billerickson.net/full-and-wide-alignment-in-gutenberg/
|
||||
[5]: https://opensource.com/sites/default/files/uploads/wp_theme_2.png (Wordpress theme)
|
||||
[6]: https://opensource.com/sites/default/files/uploads/themify_3.png (Themify screenshot)
|
||||
[7]: https://elements.envato.com/
|
||||
[8]: https://themify.me/
|
||||
[9]: https://hostadvice.com/how-to/how-to-install-a-wordpress-theme-using-cpanel/
|
||||
[10]: https://developer.wordpress.org/themes/advanced-topics/child-themes/
|
||||
[11]: https://opensource.com/sites/default/files/uploads/plugins_4.png (Plugin update page)
|
||||
[12]: https://tools.pingdom.com/
|
||||
[13]: https://www.wpbeginner.com/plugins/how-to-deactivate-all-plugins-when-not-able-to-access-wp-admin/
|
||||
[14]: https://mhthemes.com/support/knb/solving-common-cache-issues-on-wordpress-websites/
|
||||
[15]: https://jetpack.com/upgrade/backup/?utm_source=google&utm_campaign=google_jetpack_search_brand_desktop_sg_en&utm_medium=paid_search&utm_term=%2Bwordpress%20%2Bjetpack%20%2Bbackup&creative=379260213317&campaignid=2061290863&utm_content=77066462603&matchtype=b&device=c&network=g&gclid=Cj0KCQjwu6fzBRC6ARIsAJUwa2RuPx5Dzr72eBEtZegsf11MmOBgLiwLX2HcEUXVaULIgv1MdZqGmeAaArmFEALw_wcB&gclsrc=aw.ds
|
||||
@@ -1,99 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (New open source GIS projects for Kubernetes applications)
|
||||
[#]: via: (https://opensource.com/article/20/4/gis-kubernetes)
|
||||
[#]: author: (Adam Timm https://opensource.com/users/timmam)
|
||||
|
||||
New open source GIS projects for Kubernetes applications
|
||||
======
|
||||
pg_tileserv and pg_featureserv make it easier for developers to add
|
||||
location services to Kubernetes applications.
|
||||
![A map with a route highlighted][1]
|
||||
|
||||
Spatial data from geographic information systems (GIS) is all around us. From smartphones that make our lives better and more convenient to precision agriculture that is increasing the amount of food farmers can produce while reducing the cost, whether or not we realize it, almost every part of our lives is touched by spatial data.
|
||||
|
||||
This increase of spatial data is simultaneously bringing an increase of open spatial datasets that people can consume and use to build all sorts of new applications. However, these types of datasets have not always been easy to work with. Also, due to the size of some of the geographic data, they can be difficult to bring to modern application deployment frameworks such as Kubernetes.
|
||||
|
||||
To help with these issues, [Crunchy Data][2] recently announced two new open source projects, [pg_tileserv][3] and [pg_featureserv][4], to make it easier to develop cloud-native spatial applications. These projects, part of open source [Crunchy Spatial][5], help developers leverage the robust [PostGIS][6] geospatial database extension to [PostgreSQL][7] without having to write complex SQL statements.
|
||||
|
||||
So what are pg_tileserv and pg_featuresev, how do they make it easier for developers to add location services to their Kubernetes applications, and what does this mean for the future of spatial applications?
|
||||
|
||||
### Traditional GIS vs. modern spatial microservices
|
||||
|
||||
Traditionally, when an organization or individual works with spatial data, they start with a product that grew up as a GIS. There are many high-quality open source GIS products ([QGIS][8], [GeoServer][9], [GeoNode][10], etc.), but they may not align with modern, cloud-native approaches to software design.
|
||||
|
||||
The popularity of Kubernetes creates challenges for these legacy applications around automation and deployment, as they require a lot of manual configuration, for example, when data sources are added and modified. In many setups, these spatial applications must exist outside Kubernetes and cannot leverage many of the conveniences it provides.
|
||||
|
||||
In contrast, modern spatial services should be driven by the spatial data that they are processing and serving out. They should align with modern software development practices and scale efficiently and integrate easily with developer workflows.
|
||||
|
||||
Applications that are spatially aware also need to ensure they can handle the unique characteristics of spatial data (e.g., geometries, projections, etc.). To do all of this in independent microservices can be challenging unless you have a highly capable database to do the majority of the work for you. This is where pg_tileserv and pg_featureserv help, as both projects leverage the power of PostGIS, an open source geospatial extension to PostgreSQL, to provide advanced spatial capabilities from a simple REST framework
|
||||
|
||||
### Generate map vector tiles with pg_tileserv
|
||||
|
||||
![pg_tileserv][11]
|
||||
|
||||
pg_tileserv is a lightweight vector tile server written in Go that enables you to generate [vector tiles][12] directly from PostGIS. It does this by implementing the **ST_AsMVT()** function in a best-practice method that translates an HTTP request to the database. It includes common defaults that allow you to pass a database connection URL to the server and be up and running in no time. There's no heavyweight software to install and configure, and it's designed for cloud-native GIS applications.
|
||||
|
||||
For specific examples on how to use it, check out our blog posts on [tile serving][13] and [spatial tile serving with PostgreSQL functions][14].
|
||||
|
||||
### Annotate your maps with pg_featureserv
|
||||
|
||||
![pg_featureserv][15]
|
||||
|
||||
pg_featureserv is a lightweight service written in Go that enables you to serve features directly out of PostGIS. It implements the [OGC API][16] for features and provides a standard REST endpoint for your spatial data and functions contained in PostGIS. Just like pg_tileserv, there's no heavyweight software to install; just pass a database connection URL to your PostGIS database, and you're off to the races. For a specific example of how to use it, check out our post on [querying spatial features][17].
|
||||
|
||||
### Focus on spatial data, not GIS
|
||||
|
||||
With our deep background in developing PostGIS and building PostGIS-backed applications, we wanted to help developers unlock all the value of spatial data in a way that is easy to deploy, scale, and maintain. As the source code of pg_tileserv and pg_featureserv show, we are just leveraging functions already in PostGIS. This allows developers to quickly add spatial data to their applications and data scientists to focus on the data.
|
||||
|
||||
![GIS architecture][18]
|
||||
|
||||
The benefits of this approach are:
|
||||
|
||||
* Faster performance because PostgreSQL and PostGIS are doing the work for you
|
||||
* Less configuration to maintain because the database structure is the configuration
|
||||
* By design, it runs in the cloud at enterprise scale from the start
|
||||
* Shorter times to update customer-facing applications—when you update your data in the database, your application is updated instantly
|
||||
* Ability to focus more on maintaining your data and delivering value to your users and less on wrangling software
|
||||
|
||||
|
||||
|
||||
Also, since these services respond to the configuration of your database, they also expose functions contained in the database. Rather than developing their data functions to incorporate them into software later, data scientists can create functions in the database that are immediately made available via a REST API. The software begins to fade into the background so an organization can focus on the data.
|
||||
|
||||
Suffice it to say, we're pretty excited about these new geospatial services, and we definitely want your feedback on them. Feel free to check out [pg_tileserve][3] and [pg_featureserv][4], try deploying them alongside your PostGIS databases with the [PostgreSQL Operator][19], and share your feedback in the comments.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/4/gis-kubernetes
|
||||
|
||||
作者:[Adam Timm][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/timmam
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/map_route_location_gps_path.png?itok=RwtS4DsU (A map with a route highlighted)
|
||||
[2]: https://www.crunchydata.com/
|
||||
[3]: https://github.com/CrunchyData/pg_tileserv
|
||||
[4]: https://github.com/CrunchyData/pg_featureserv
|
||||
[5]: https://www.crunchydata.com/products/crunchy-spatial/
|
||||
[6]: https://postgis.net/
|
||||
[7]: https://www.postgresql.org
|
||||
[8]: https://www.qgis.org/en/site/
|
||||
[9]: http://geoserver.org/
|
||||
[10]: http://geonode.org/
|
||||
[11]: https://opensource.com/sites/default/files/pg_tileserv.jpg (pg_tileserv)
|
||||
[12]: https://info.crunchydata.com/blog/dynamic-vector-tiles-from-postgis
|
||||
[13]: https://info.crunchydata.com/blog/crunchy-spatial-tile-serving
|
||||
[14]: https://info.crunchydata.com/blog/crunchy-spatial-tile-serving-with-postgresql-functions
|
||||
[15]: https://opensource.com/sites/default/files/pg_featureserv.jpg (pg_featureserv)
|
||||
[16]: http://www.ogcapi.org/
|
||||
[17]: https://info.crunchydata.com/blog/crunchy-spatial-querying-spatial-features-with-pg_featureserv
|
||||
[18]: https://opensource.com/sites/default/files/uploads/architecture_0.png (GIS architecture)
|
||||
[19]: https://github.com/CrunchyData/postgres-operator
|
||||
@@ -1,99 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How I use Hugo for my classroom's open source CMS)
|
||||
[#]: via: (https://opensource.com/article/20/4/hugo-classroom)
|
||||
[#]: author: (Peter Cheer https://opensource.com/users/petercheer)
|
||||
|
||||
How I use Hugo for my classroom's open source CMS
|
||||
======
|
||||
This open source software streamlines text editing while leaving room
|
||||
for customization.
|
||||
![Digital hand surrounding by objects, bike, light bulb, graphs][1]
|
||||
|
||||
People love Markdown text with good reason—it is easy to write, easy to read, easy to edit, and it can be converted to a wide range of other text mark up formats. While Markdown text is very good for content creation and manipulation, it imposes limitations on the options for content display.
|
||||
|
||||
If we could combine the virtues of Markdown with the power and flexibility of Cascading Style Sheets, HTML5, and JavaScript, that would be something special. One of the programs trying to do this is [Hugo][2]. Hugo was created in 2013 by Steve Francia; it is cross-platform and open source under an Apache 2.0 license with an active developer community and a growing user base.
|
||||
|
||||
The basic concept is that pieces of content, such as web pages or blog posts, written in Markdown and associated with metadata, are converted into HTML and combined with templates and themes to produce a complete web site. The power and flexibility come through these themes and templates or changing the default behaviors of Hugo. This power comes with a degree of unavoidable complexity, but there are lots of [pre-built templates][3] available if you lack the time or inclination to make your own.
|
||||
|
||||
Installing Hugo on my Linux machine was quick and easy. Starting a new project is as simple as typing **hugo new site quickstart** at the command line which creates a new project with this folder structure:
|
||||
|
||||
* **archetypes**: Content template files that contain preconfigured front matter metadata (date, title, draft). You can create new archetypes with custom front matter fields.
|
||||
* **assets**: Stores all the files, which are processed by Hugo Pipes (e.g., CSS/Sass files). This directory is not created by default.
|
||||
* **config.toml**: The default site config file.
|
||||
* **content**: Where all the content Markdown files live.
|
||||
* **data**: Used to store configuration files that can be used by Hugo when generating your website.
|
||||
* **layouts**: Stores templates as .html files.
|
||||
* **static**: Stores all the static content—images, CSS, JavaScript, etc.
|
||||
* **themes**: For the Hugo theme of your choice.
|
||||
|
||||
|
||||
|
||||
The Markdown files in the content folder can be created manually or by Hugo and edited with any text editor or your Markdown creation tool of choice. If created manually, you will need to add any metadata that is needed. I prefer to use [Ghostwriter][4] for writing Markdown. Images are usually kept in a sub-folder in the static folder. Site development can proceed quickly, as Hugo includes a web server for testing and pre-viewing.
|
||||
|
||||
To check your work, type **hugo server** at the command line to start the server. By default, Hugo will not publish:
|
||||
|
||||
* Content with a future **publishdate** value.
|
||||
* Content with **draft: true** status.
|
||||
* Content with a past **expirydate** value.
|
||||
|
||||
|
||||
|
||||
Adding **hugo server -D** will include draft articles, and Hugo can be configured to mark all new articles as draft. After starting the web server, you can see your work in a web browser at localhost:1313. Once the server is started by default, it will automatically reload the browser window when it detects a change to one of your files.
|
||||
|
||||
There are tasks Markdown cannot do that need some HTML code. Hugo recognizes this but believes in keeping Markdown code as clean, simple, and uncluttered as possible. Hugo does this with shortcodes such as **{{< youtube id= "w7Ft2ymGmfc" autoplay= "true">}}**, which will embed the YouTube video with id. w7Ft2ymGmfc. There are quite a few pre-built shortcodes for common tasks, but it is also possible to create your own for particular jobs.
|
||||
|
||||
I work in education quite a lot and wanted to include some interactive puzzles and questions on my Hugo-generated website. To get the output looking like this:
|
||||
|
||||
![JClic shortcode][5]
|
||||
|
||||
I created the activities with an open source Java program called [JClic][6], exported them as HTML5, put that into static/activities/excel, and displayed it in an iframe.
|
||||
|
||||
The HTML code, which would spoil the nice clean Markdown content, looks like this:
|
||||
|
||||
|
||||
```
|
||||
<[iframe][7]
|
||||
src="/activity/excel/index.html"
|
||||
title="Activity"
|
||||
height="400"
|
||||
frameborder="0"
|
||||
marginwidth="0"
|
||||
marginheight="0"
|
||||
scrolling="no"
|
||||
style="border: 1px solid #CCC; border-width: 1px; margin-bottom: 20px; width: 100%;"
|
||||
allowfullscreen="true">
|
||||
</[iframe][7]>
|
||||
```
|
||||
|
||||
The code is saved in layouts/shortcodes as **activity.html**
|
||||
|
||||
This makes the shortcode placed inside my Markdown file **{{<activity>}}**, which is much neater.
|
||||
|
||||
When your project is ready, you can build it with the **hugo** command; this will create a public folder and generate the website in it. Hugo has a number of built-in deployment options for different hosting providers—basically, you deploy your site by copying the public folder to your production web server. There is a lot more to Hugo that I haven't even gotten to yet, including configuration options, importing content from other static site generators and Wordpress, display data from JSON files, syntax highlighting of source code, and the fact that it is very fast (an advantage when working with large sites).
|
||||
|
||||
In many software tools, ease-of-use comes at the expense of flexibility, or vice-versa; Hugo makes a largely successful attempt at including both. For basic use with Markdown content and a pre-built theme, Hugo is easy to use and produces rapid results. Alternatively, if you have the need to alter the configuration settings or dive in and create your own themes, shortcodes, templates, or metadata schemes, that choice is open to you.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/4/hugo-classroom
|
||||
|
||||
作者:[Peter Cheer][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/petercheer
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/rh_003588_01_rd3os.combacktoschoolseriesk12_rh_021x_0.png?itok=fvorN0e- (Digital hand surrounding by objects, bike, light bulb, graphs)
|
||||
[2]: https://gohugo.io/
|
||||
[3]: https://themes.gohugo.io/
|
||||
[4]: http://github.com/wereturtle/ghostwriter
|
||||
[5]: https://opensource.com/sites/default/files/uploads/jclic_shortcode.png (JClic shortcode)
|
||||
[6]: https://clic.xtec.cat/legacy/en/index.html
|
||||
[7]: http://december.com/html/4/element/iframe.html
|
||||
@@ -1,273 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to take advantage of Linux's extensive vocabulary)
|
||||
[#]: via: (https://www.networkworld.com/article/3539011/how-to-takke-advantage-of-linuxs-extensive-vocabulary.html)
|
||||
[#]: author: (Sandra Henry-Stocker https://www.networkworld.com/author/Sandra-Henry_Stocker/)
|
||||
|
||||
How to take advantage of Linux's extensive vocabulary
|
||||
======
|
||||
Linux systems don't only know a lot of words, it has commands that can help you use them by finding words that are on the tip of your tongue or fixing your typos.
|
||||
Sandra Henry-Stocker
|
||||
|
||||
While you might not think of Linux as a writing tutor, it does have some commendable language skills – at least when it comes to English. While the average American probably has a vocabulary between 20,000 and 50,000 words, Linux can claim over 100,000 words (spellings, not definitions). And you can easily put this vocabulary to work for you in a number of ways. Let’s look at how Linux can help with your word challenges.
|
||||
|
||||
### Help with finding words
|
||||
|
||||
First, let’s focus on finding words.If you use the **wc** command to count the number of words in the **/usr/share/dict/words** file on your system, you should see something like this:
|
||||
|
||||
```
|
||||
$ wc -l /usr/share/dict/words
|
||||
102402 /usr/share/dict/words
|
||||
```
|
||||
|
||||
As you can see, the **words** file on this system contains 102,402 words. So, when you’re trying to nail down just the right word and are having trouble, you stand a good chance of finding it on your system by remembering (or guessing at) some part of it. But you'll need a little help narrowing down those 102,402 words to a group worth your time to review. In this command, we’re looking for words that start with the letters “revi”.
|
||||
|
||||
[[Get regularly scheduled insights by signing up for Network World newsletters.]][1]
|
||||
|
||||
```
|
||||
$ grep ^reviv /usr/share/dict/words
|
||||
revival
|
||||
revival's
|
||||
revivalist
|
||||
revivalist's
|
||||
revivalists
|
||||
revivals
|
||||
revive
|
||||
revived
|
||||
revives
|
||||
revivification
|
||||
revivification's
|
||||
revivified
|
||||
revivifies
|
||||
revivify
|
||||
revivifying
|
||||
reviving
|
||||
```
|
||||
|
||||
That’s sixteen words that start with the string “revi”. The **^** character represents the beginning of the word and, as you might have suspected, each word in the file is on a line by itself.
|
||||
|
||||
A good number of the words in the **/usr/share/dict/words** file are names. If you want to find words regardless of whether they're capitalized, add the **-i** (ignore case) option to your **grep** command.
|
||||
|
||||
```
|
||||
$ grep -i ^wool /usr/share/dict/words
|
||||
Woolf
|
||||
Woolf's
|
||||
Woolite
|
||||
Woolite's
|
||||
Woolongong
|
||||
Woolongong's
|
||||
Woolworth
|
||||
Woolworth's
|
||||
wool
|
||||
...
|
||||
```
|
||||
|
||||
You can also look for words that end in or contain a certain string of letters. In this next command, we look for words that contain the string “nativ” at any location.
|
||||
|
||||
```
|
||||
$ grep 'nativ' /usr/share/dict/words
|
||||
alternative
|
||||
alternative's
|
||||
alternatively
|
||||
alternatives
|
||||
imaginative
|
||||
imaginatively
|
||||
native
|
||||
native's
|
||||
natives
|
||||
nativities
|
||||
nativity
|
||||
nativity's
|
||||
nominative
|
||||
nominative's
|
||||
nominatives
|
||||
unimaginative
|
||||
```
|
||||
|
||||
In this next command, we look for words that end in “emblance”, the **$** character representing the end of the line. Only two words in the **words** file fit the bill.
|
||||
|
||||
[][2]
|
||||
|
||||
```
|
||||
$ grep 'emblance$' /usr/share/dict/words
|
||||
resemblance
|
||||
semblance
|
||||
```
|
||||
|
||||
If we, for some reason, want to find words with exactly 21 letters, we could use this command:
|
||||
|
||||
```
|
||||
$ grep '^.....................$' /usr/share/dict/words
|
||||
counterintelligence's
|
||||
electroencephalograms
|
||||
electroencephalograph
|
||||
```
|
||||
|
||||
On the other hand, making sure we've typed the correct number of dots can be tedious. This next command is little easier to manage:
|
||||
|
||||
```
|
||||
$ grep -E '^[[:alpha:]]{21}$' /usr/share/dict/words
|
||||
electroencephalograms
|
||||
electroencephalograph
|
||||
```
|
||||
|
||||
This command does the same thing:
|
||||
|
||||
```
|
||||
$ grep -E '^\w{21}$' /usr/share/dict/words
|
||||
electroencephalograms
|
||||
electroencephalograph
|
||||
```
|
||||
|
||||
The one important difference between these commands is that the one with the dots matches any string of 21 characters. The two specifying "alpha" or "\w" only match letters, so they find only two matching words.
|
||||
|
||||
Now let’s look for words that contain 20 letters (or more) in a row.
|
||||
|
||||
```
|
||||
$ grep -E '(\w{20})' /usr/share/dict/words
|
||||
Andrianampoinimerina
|
||||
Andrianampoinimerina's
|
||||
counterrevolutionaries
|
||||
counterrevolutionary
|
||||
counterrevolutionary's
|
||||
electroencephalogram
|
||||
electroencephalogram's
|
||||
electroencephalograms
|
||||
electroencephalograph
|
||||
electroencephalograph's
|
||||
electroencephalographs
|
||||
uncharacteristically
|
||||
```
|
||||
|
||||
That command returns words with apostrophes because they contain 20 letters in a row before they get to that point.
|
||||
|
||||
Next, we’ll check out words with 21 or more characters. The 1 and 20 in combination with the **v** (invert) option in this command cause **grep** to skip over words with anywhere from 1 to 20 characters.
|
||||
|
||||
```
|
||||
$ grep -vwE '\w{1,20}' /usr/share/dict/words
|
||||
counterrevolutionaries
|
||||
electroencephalograms
|
||||
electroencephalograph
|
||||
electroencephalographs
|
||||
```
|
||||
|
||||
In this next command, we look for words that start with “ex” and have four additional letters.
|
||||
|
||||
```
|
||||
$ grep '^ex.\{4\}$' /usr/share/dict/words
|
||||
exacts
|
||||
exalts
|
||||
exam's
|
||||
exceed
|
||||
excels
|
||||
except
|
||||
excess
|
||||
excise
|
||||
excite
|
||||
excuse
|
||||
…
|
||||
```
|
||||
|
||||
In case you're curious, the **words** file on this system contains 43 such words:
|
||||
|
||||
```
|
||||
$ grep '^ex.\{4\}$' /usr/share/dict/words | wc -l
|
||||
43
|
||||
```
|
||||
|
||||
To get help with spelling, you should try **aspell**. It can help you with individual words or run a spell check scan through an entire text file. In this first example, we ask **aspell** to help with a single word. It finds the word we’re after along with a couple other possibilities.
|
||||
|
||||
### Checking a word
|
||||
|
||||
```
|
||||
$ aspell -a
|
||||
@(#) International Ispell Version 3.1.20 (but really Aspell 0.60.7)
|
||||
prolifferate <== entered word
|
||||
& prolifferate 3 0: proliferate, proliferated, proliferates <== replacement options
|
||||
```
|
||||
|
||||
If **aspell** doesn’t provide a list of words, that means that the spelling you offered was correct. Here's an example:
|
||||
|
||||
```
|
||||
$ aspell -a
|
||||
@(#) International Ispell Version 3.1.20 (but really Aspell 0.60.7)
|
||||
proliferate <== entered text
|
||||
* <== no suggestions
|
||||
```
|
||||
|
||||
Typing **^C** (control-c) exits **aspell**.
|
||||
|
||||
### Checking a file
|
||||
|
||||
When checking a file with **aspell**, you get suggestions for each misspelled word. When **aspell** spots typos, it highlights the misspelled words one at a time and gives you a chance to choose from a list of properly spelled words that are similar enough to the misspelled words to be good candidates for replacing them.
|
||||
|
||||
To start checking a file, type **aspell -c** followed by the file name.
|
||||
|
||||
```
|
||||
$ aspell -c thesis
|
||||
```
|
||||
|
||||
You'll see something like this:
|
||||
|
||||
```
|
||||
This thesis focusses on …
|
||||
|
||||
1) focuses 6) Fosse's
|
||||
2) focused 7) flosses
|
||||
3) cusses 8) courses
|
||||
4) fusses 9) focus
|
||||
5) focus's 0) fuses
|
||||
i) Ignore I) Ignore all
|
||||
r) Replace R) Replace all
|
||||
a) Add l) Add Lower
|
||||
b) Abort x) Exit
|
||||
```
|
||||
|
||||
Make your selection by pressing the key listed next to the word you want (1, 2, etc.) and **aspell** will replace the misspelled word in the file and move on to the next one if there are others. Notice that you also have options to replace the word by typing another one. Press "x" when you're done.
|
||||
|
||||
### Help with crossword puzzles
|
||||
|
||||
If you’re working on a crossword puzzle and need to find a five-letter word that starts with a “d” and has a “u” as its fourth letter, you can use a command like this:
|
||||
|
||||
```
|
||||
$ grep -i '^d..u.$' /usr/share/dict/words
|
||||
datum
|
||||
debug
|
||||
debut
|
||||
demur
|
||||
donut
|
||||
```
|
||||
|
||||
### Help with word scrambles
|
||||
|
||||
If you’re working on a puzzle that requires you to de-scramble the letters in a string until you've found a proper word, you can offer the list of letters to grep like this example in which **grep** turns the letters "yxusonlia" into the word “anxiously”.
|
||||
|
||||
```
|
||||
$ grep -P '^(?:([yxusonlia])(?!.*?\1)){9}$' /usr/share/dict/words
|
||||
anxiously
|
||||
```
|
||||
|
||||
Linux’s word skills are impressive and sometimes even fun. Whether you're hoping to find words you can't quite call to mind or get a little help cheating on word puzzles, Linux offers some clever options.
|
||||
|
||||
Join the Network World communities on [Facebook][3] and [LinkedIn][4] to comment on topics that are top of mind.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3539011/how-to-takke-advantage-of-linuxs-extensive-vocabulary.html
|
||||
|
||||
作者:[Sandra Henry-Stocker][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Sandra-Henry_Stocker/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.networkworld.com/newsletters/signup.html
|
||||
[2]: https://www.networkworld.com/blog/itaas-and-the-corporate-storage-technology/?utm_source=IDG&utm_medium=promotions&utm_campaign=HPE22140&utm_content=sidebar (ITAAS and Corporate Storage Strategy)
|
||||
[3]: https://www.facebook.com/NetworkWorld/
|
||||
[4]: https://www.linkedin.com/company/network-world
|
||||
@@ -1,121 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (New zine: How Containers Work!)
|
||||
[#]: via: (https://jvns.ca/blog/2020/04/27/new-zine-how-containers-work/)
|
||||
[#]: author: (Julia Evans https://jvns.ca/)
|
||||
|
||||
New zine: How Containers Work!
|
||||
======
|
||||
|
||||
On Friday I published a new zine: “How Containers Work!”. I also launched a fun redesign of [wizardzines.com][1].
|
||||
|
||||
You can get it for $12 at <https://wizardzines.com/zines/containers>. If you buy it, you’ll get a PDF that you can either print out or read on your computer. Or you can get a pack of [all 8 zines][2] so far.
|
||||
|
||||
Here’s the cover and table of contents:
|
||||
|
||||
[![][3]][4] <https://jvns.ca/images/containers-toc.png>
|
||||
|
||||
### why containers?
|
||||
|
||||
I’ve spent a lot of time [figuring][5] [out][6] [how to][7] [run][8] [things][9] [in][10] [containers][11] over the last 3-4 years. And at the beginning I was really confused! I knew a bunch of things about Linux, and containers didn’t seem to fit in with anything I thought I knew (“is it a process? what’s a network namespace? what’s happening?“). The whole thing seemed really weird.
|
||||
|
||||
It turns out that containers ARE actually pretty weird. They’re not just one thing, they’re what you get when you glue together 6 different features that were mostly designed to work together but have a bunch of confusing edge cases.
|
||||
|
||||
As usual, the thing that helped me the most in my container adventures is a good understanding of the **fundamentals** – what exactly is actually happening on my server when I run a container?
|
||||
|
||||
So that’s what this zine is about – cgroups, namespaces, pivot_root, seccomp-bpf, and all the other Linux kernel features that make containers work.
|
||||
|
||||
Once I understood those ideas, it got a **lot** easier to debug when my containers were doing surprising things in production. I learned a couple of interesting and strange things about containers while writing this zine too – I’ll probably write a blog post about one of them later this week.
|
||||
|
||||
### containers aren’t magic
|
||||
|
||||
This picture (page 6 of the zine) shows you how to run a fish container image with only 15 lines of bash. This is heavily inspired by [bocker][12], which “implements” Docker in about 100 lines of bash.
|
||||
|
||||
<https://jvns.ca/images/containers-arent-magic.jpg>
|
||||
|
||||
The main things I see missing from that script compared to what Docker actually does when running a container (other than using an actual container image and not just a tarball) are:
|
||||
|
||||
* it doesn’t drop any capabilities – the container is still running as root and has full root privileges (just in a different mount + PID namespace)
|
||||
* it doesn’t block any system calls with seccomp-bpf
|
||||
|
||||
|
||||
|
||||
### container command line tools
|
||||
|
||||
The zine also goes over a bunch of command line tools & files that you can use to inspect running containers or play with Linux container features. Here’s a list:
|
||||
|
||||
* `mount -t overlay` (create and view overlay filesystems)
|
||||
* `unshare` (create namespaces)
|
||||
* `nsenter` (use an existing namespace)
|
||||
* `getpcaps` (get a process’s capabilities)
|
||||
* `capsh` (drop or add capabilities, etc)
|
||||
* `cgcreate` (create a cgroup)
|
||||
* `cgexec` (run a command in an existing cgroup)
|
||||
* `chroot` (change root directory. not actually what containers use but interesting to play with anyway)
|
||||
* `/sys/fs/cgroups` (for information about cgroups, like `memory.usage_in_bytes`)
|
||||
* `/proc/PID/ns` (all a process’s namespaces)
|
||||
* `lsns` (another way to view namespaces)
|
||||
|
||||
|
||||
|
||||
I also made a short youtube video a while back called [ways to spy on a Docker container][13] that demos some of these command line tools.
|
||||
|
||||
### container runtime agnostic
|
||||
|
||||
I tried to keep this zine pretty container-runtime-agnostic – I mention Docker a couple of times because it’s so widely used, but it’s about the Linux kernel features that make containers work in general, not Docker or LXC or systemd-nspawn or Kubernetes or whatever. If you understand the fundamentals you can figure all those things out!
|
||||
|
||||
### we redesigned wizardzines.com!
|
||||
|
||||
On Friday I also launched a redesign of [wizardzines.com][1]! [Melody Starling][14] (who is amazing) did the design. I think now it’s better organized but the tiny touch that I’m most delighted by is that now the zines jump with joy when you hover over them.
|
||||
|
||||
One cool thing about working with a designer is – they don’t just make things _look_ better, they help _organize_ the information better so the website makes more sense and it’s easier to find things! This is probably obvious to anyone who knows anything about design but I haven’t worked with designers very much (or maybe ever?) so it was really cool to see.
|
||||
|
||||
One tiny example of this: Melody had the idea of adding a tiny FAQ on the landing page for each zine, where I can put the answers to all the questions people always ask! Here’s what the little FAQ box looks like:
|
||||
|
||||
[![][15]][4]
|
||||
|
||||
I probably want to edit those questions & answers over time but it’s SO NICE to have somewhere to put them.
|
||||
|
||||
### what’s next: maybe debugging! or working more on flashcards!
|
||||
|
||||
The two projects I’m thinking about the most right now are
|
||||
|
||||
1. a zine about debugging, which I started last summer and haven’t gotten around to finishing yet
|
||||
2. a [flashcards project][16] that I’ve been adding to slowly over the last couple of months. I think could become a nice way to explain basic ideas.
|
||||
|
||||
|
||||
|
||||
Here’s a link to where to [get the zine][4] again :)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://jvns.ca/blog/2020/04/27/new-zine-how-containers-work/
|
||||
|
||||
作者:[Julia Evans][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://jvns.ca/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://wizardzines.com
|
||||
[2]: https://wizardzines.com/zines/all-the-zines/
|
||||
[3]: https://jvns.ca/images/containers-cover.jpg
|
||||
[4]: https://wizardzines.com/zines/containers
|
||||
[5]: https://stripe.com/en-ca/blog/operating-kubernetes
|
||||
[6]: https://jvns.ca/blog/2016/09/15/whats-up-with-containers-docker-and-rkt/
|
||||
[7]: https://jvns.ca/blog/2016/10/10/what-even-is-a-container/
|
||||
[8]: https://jvns.ca/blog/2016/12/22/container-networking/
|
||||
[9]: https://jvns.ca/blog/2016/10/26/running-container-without-docker/
|
||||
[10]: https://jvns.ca/blog/2017/02/17/mystery-swap/
|
||||
[11]: https://jvns.ca/blog/2016/10/02/a-list-of-container-software/
|
||||
[12]: https://github.com/p8952/bocker
|
||||
[13]: https://www.youtube.com/watch?v=YCVSdnYzH34&t=1s
|
||||
[14]: https://melody.dev
|
||||
[15]: https://jvns.ca/images/wizardzines-faq.png
|
||||
[16]: https://flashcards.wizardzines.com
|
||||
@@ -1,161 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Edit music recordings with Audacity on Linux)
|
||||
[#]: via: (https://opensource.com/article/20/4/audacity)
|
||||
[#]: author: (David Both https://opensource.com/users/dboth)
|
||||
|
||||
Edit music recordings with Audacity on Linux
|
||||
======
|
||||
How COVID-19 caused me to learn Audacity on the fly and learn to love
|
||||
it.
|
||||
![Bird singing and music notes][1]
|
||||
|
||||
In this strange and difficult time of a global pandemic, we are all called upon to do things differently, to change our routines, and to learn new things.
|
||||
|
||||
I have worked from home for many years, so that is nothing new to me. Even though I am allegedly retired, I write articles for Opensource.com and [Enable Sysadmin][2] and books. I also manage my own home network, which is larger than you might think, and my church's network and Linux hosts, and I help a few friends with Linux. All of this keeps me busy doing what I like to do, and all of it is usually well within my comfort zone.
|
||||
|
||||
But COVID-19 has changed all of that. And, like many other types of organizations, my church had to move quickly to a new service-delivery paradigm. And that is what churches do—deliver a specific kind of service. As the church sysadmin and with some knowledge of audio recording and editing (back in the '70s, I mixed the sound and was the only roadie for a couple of regional folk-rock groups in Toledo, Ohio), I decided to learn the open source audio recording and editing software [Audacity][3] to help meet this challenge.
|
||||
|
||||
This is not a comprehensive how-to article about using Audacity. It is about my experiences getting started with this powerful audio-editing tool, but there should be enough information here to help you get started.
|
||||
|
||||
I have learned just what I need to know in order to accomplish my task: combining several separate audio clips into a single MP3 audio file. If you already know Audacity and do things differently or know things that I don't, that is expected. And if you have any suggestions to help me accomplish my task more easily, please share them in the comments.
|
||||
|
||||
### The old way
|
||||
|
||||
I try not to use the term "normal" now because it is hard to know exactly what that is—if such a state even exists. But our old method of producing recordings for our shut-ins, members who are traveling, and anyone else was to record the sermon portion of our regular, in-person church services and post them on our website.
|
||||
|
||||
To do this, I installed a TASCAM SS-R100 solid-state recorder that stores the sermons as MP3 files on a thumb drive. We uploaded the recordings to a special directory of our website so people could download them. The recordings are uploaded using a Bash [program][4] I wrote for the task. _Automate everything!_ I trained a couple of others to perform these tasks using sudo in case I was not available.
|
||||
|
||||
This all worked very well. Until it didn't.
|
||||
|
||||
### The new way
|
||||
|
||||
As soon as the first restrictions on large gatherings occurred, we made some changes. We could still have small gatherings, so four of us met Sunday mornings and recorded an abbreviated service using our in-house recorder and doing the upload the usual way. This worked, but as the crisis deepened and it became more of a risk to meet with even a few people, we had to make more changes.
|
||||
|
||||
Like a huge number of other organizations, we realized we each needed to perform our parts of creating services in separate locations from our own homes.
|
||||
|
||||
Now, depending upon the structure of the service, I receive several recordings that I need to combine to create the full church service. Our music director records each anthem and interlude using her iPhone and sends me the recordings in the M4A (MPEG-4 audio) format. They each range in length from seconds to five minutes and are up to 3MB in size. Likewise, our rector sends me two to six recordings, also in M4A format, that contains his portion of the service. Sometimes, other musicians in our church send solos or duets recorded with their significant others; these can be in MP3 or M4A formats.
|
||||
|
||||
Then, I pull all of this together into a single recording that can be uploaded to our server for people to download. I use Audacity for this because it was available in my repo, and it was easy to get started.
|
||||
|
||||
### Getting started with Audacity
|
||||
|
||||
I had never used [Audacity][5] before this, so, like many others these days, I needed to learn something new just in time to accomplish what I needed to do. I struggled a bit at first, but it turned out to be fun and very enlightening.
|
||||
|
||||
Audacity was easy to install on my Fedora 31 workstation because, as in many distros, it is available from the Fedora repository.
|
||||
|
||||
The first time I opened Audacity with the program launcher icon, the application's window was empty with no projects nor tracks present. Audacity projects have an AUP extension, so if you have an existing project, you could click on the file in your favorite file manager and launch Audacity that way.
|
||||
|
||||
### Convert M4A to MP3
|
||||
|
||||
As installed by Fedora, Audacity does not recognize M4A files. Regardless of how you proceed, you need to install the [LAME][6] MP3 encoder and [FFmpeg][7] import/export library, both of which are available from the Fedora repository and, most likely, any other distro's repository.
|
||||
|
||||
There are websites that explain how to configure Audacity to use these tools to import and convert audio files from M4A to other types (such as MP3), but I decided to write a script to do it from the command line. For one reason, using a script is faster than doing a lot of extra clicking in a GUI interface, and for another, the file names need some work, so I already needed a script to rename the files. Many people use non-alphanumeric characters to name files, but I don't like dealing with special keyboard characters from the command line. It's easier to manage files with simple alphanumeric names, so my script removes all non-alphanumeric characters from the file names and then converts the files to MP3 format.
|
||||
|
||||
You may choose a different approach, but I like the scripted solution. It is fast, and I only need to run the script once, no matter how many files need to be renamed and converted to MP3.
|
||||
|
||||
### Create a new project
|
||||
|
||||
You can create a new project whether or not any audio tracks are loaded. I recommend creating the project first, before importing any audio files (aka "clips"). From the Menu bar, select **File > Save Project > Save Project As**. This opens a warning dialog window that says, _"'Save project' is for an Audacity project, not an audio file."_ Click the **OK** button to continue to a standard file-save dialog.
|
||||
|
||||
I found that I needed to do this twice. The first time, the warning dialog did not display any buttons, so I had to close the dialog using the window menu or the x icon in the Title bar.
|
||||
|
||||
Name the project whatever you like, and Audacity automatically adds the AUP extension. You now have an empty project.
|
||||
|
||||
### Add audio files to your project
|
||||
|
||||
The first step is to add your audio files to the project. Using the Menu bar, open **File > Import > Audio** and then use the file dialog to select one or more files to import. For my first test project, I loaded all the files at once without sorting the tracks nor aligning the clips in the desired sequence along the timeline. This time, I started by loading the audio files one at a time in the sequence I wanted them from top to bottom. As each file is imported, it is placed into a new track below any existing tracks. The following image shows the files loaded all at one time in the sequence they appear in the working directory.
|
||||
|
||||
![Tracks loaded in Audacity][8]
|
||||
|
||||
There is a timeline across the top of the window's track area. There is also a scroll bar at the bottom of the window, so you can scroll along the timeline when the tracks extend beyond the width of the Audacity window. There is also a vertical scroll bar if there are more tracks than fit into the window.
|
||||
|
||||
Notice the names in the upper-left corner of the waveform section of each track—they are the file names of each track without the extension. These are not there by default, but I find them helpful. To display these names, use the Menu bar to select **Edit > Preferences** and place a check in the **Show Audio Track Name As Overlay** box.
|
||||
|
||||
### Order your audio clips
|
||||
|
||||
Once you have some files loaded into the Audacity workspace, you can start manipulating them. To order your audio clips, select one and use the **Time-Shift** tool (↔︎) to slide them horizontally along the tracks; continue doing this until all the clips line up end to end in the order you want them. Note that the clip you are moving is book-ended by a pair of vertical alignment lines. When they line up perfectly, the end lines of the two aligned tracks change color to alert you.
|
||||
|
||||
You can hover the mouse pointer over the tool icons in the Audacity toolbars to see a pop-up that displays the name of that tool. This helps beginners understand what each tool does.
|
||||
|
||||
![Audacity toolbox][9]
|
||||
|
||||
Here, the **Selection** tool** **is selected in the Audacity toolbar. The **Time-Shift** tool is second from the left on the bottom row.
|
||||
|
||||
The following image shows what happens when you slide the audio clips into place on the project timeline without sorting the tracks into a particular sequence. This may not be optimal for how you like to work. It is not for me.
|
||||
|
||||
![Audio clips in Audacity][10]
|
||||
|
||||
To remove segments of (or complete) audio clips, select them with the **Selection** tool—you can also select multiple adjacent tracks. Then you can press the **Delete** button on your keyboard to delete the selected segment(s).
|
||||
|
||||
In the image above, you can see a vertical black line in track 1 and a vertical green line crossing all the tracks. These are the audio cursors that show the playback positions of a track or the entire project. Choose the **Selection** tool and click the desired position within a track, then click the **Play** button on the transport controls (in the upper-left of the Audacity window) to begin playback. Playback will continue past the end of the selected track and all the way to the end of the project. If tracks overlap on the timeline, they will play simultaneously.
|
||||
|
||||
To begin playback immediately, click the desired starting point on the timeline. To play part of a track, hold down the Left mouse button to select a short segment of the track, and then click the **Play** button. The other transport buttons—Pause, Stop, and so—on are identified with universal icons and work as you would expect.
|
||||
|
||||
You can also click the **Silence Audio Selection** button—the fifth button from the left on the **Edit** toolbar (shown below)—to completely silence a selected segment while leaving it in place for timing purposes. This is how I silenced a number of background clicks and noises.
|
||||
|
||||
![Audacity edit tools][11]
|
||||
|
||||
It took me a while to figure out how to sort the tracks vertically, and it turns out there are a few different ways to accomplish the task.
|
||||
|
||||
You can use the track menu to reorder arrangement. Each track has its own Control Panel on the left side (shown below). The track drop-down Menu bar at the top of the Control Panel opens a menu that provides several track-sequencing options to move a track up, down, to the top, or to the bottom.
|
||||
|
||||
![Moving tracks in Audacity][12]
|
||||
|
||||
The items to move a track up or down move the track one position at a time, so you have to select it as many times as necessary to get the track in the desired position.
|
||||
|
||||
To drag and drop tracks, you must click on the space occupied by the track details. In this screenshot, that's "Mono, 48000Hz 32 bit float". It can be tricky, because if you click too high, you adjust the panning (the left and right stereo position) and if you click too low, you may collapse or select the track. Target the "Mono" or "Stereo" label (whatever your track happens to be) label, and then click and drag the track up or down to reposition it in your workspace.
|
||||
|
||||
### Apply amplification and noise reduction effects
|
||||
|
||||
Some tracks need the overall volume to be adjusted. I used the **Selection** tool to double-click and select the entire track (but you could also select a portion of a track). On the Menu bar, select **Effect > Amplify** to display a small dialog window. You can use the slider or enter a value to specify the amount of amplification. Negative numbers decrease the volume. If you try to increase the volume, you need to place a check in the **Allow Clipping** box. Then click OK.
|
||||
|
||||
I found that amplification is a bit tricky; it is easy to use too much or too little. Start by using small numbers to see the results. You can always use **Ctrl+Z** to undo your changes if you go too far in either direction.
|
||||
|
||||
Another effect I find useful is noise reduction. One of the tracks was recorded with a noticeable 60Hz hum, which is usually due to poor grounding of the microphone or recorder. Fortunately, there were only several seconds of hum and no other sound at the beginning of the recording.
|
||||
|
||||
Applying the noise reduction effect was a little confusing at first. First, I selected a few samples of the humming sound to tell Audacity what sound needed to be reduced, and then I navigated to **Effect > Noise Reduction**. This opens the **Noise Reduction** dialog. I clicked on the **Get Noise Profile** button in the Step 1 section of the dialog, which uses the selected sample as the basis for a set of filter presets. After it gathers the selected sample, though, the dialog disappeared (this is by design). I re-opened the dialog, used the slider to select the noise reduction level in decibels (I set it to 15dB and left the other sliders alone), and then clicked **OK**.
|
||||
|
||||
This worked well—you can hear the residual hum only if you know it is there. I need to experiment with this some more, but since the result was acceptable, so I did not play with the settings any further.
|
||||
|
||||
The reason the dialog box closes after getting a noise profile is actually for the sake of expediency. If you're processing many tracks or segments of audio, each with a different noise profile, you can open the **Noise Reduction** effect, get the current noise profile, and then select the audio you want to clean. You can then run the Noise Reduction filter using **Ctrl+R**, the keyboard shortcut for running the most recent filter. Instead of getting a new noise profile, however, Audacity uses the one you've just stored, and performs the filter instead. This way, you can get a sample with a few clicks but clean lots of audio with just one keyboard shortcut.
|
||||
|
||||
### And so much more
|
||||
|
||||
I have only worked with a few of the basics and have not even begun to scratch the surface of Audacity. I can already see that it has so many more features and tools that will enable me to create even more professional-sounding projects.
|
||||
|
||||
For example, in addition to working with existing audio files, Audacity can make recordings from line inputs, the desktop sound stream, and microphone inputs. It can do special effects like fade in and out and cross-fades. And I have not even tried to figure out what many of the other effects and tools are capable of.
|
||||
|
||||
I have a feeling I will need to learn more in the near future. Hopefully, this story of my very limited experience with Audacity will prompt you to check it out. For much more information, you can find the [Audacity manual][13] online.
|
||||
|
||||
Using Audacity, you can quickly clean up audio file so that any background noise becomes tolerable.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/4/audacity
|
||||
|
||||
作者:[David Both][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/dboth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/music-birds-recording-520.png?itok=UoM7brl0 (Bird singing and music notes)
|
||||
[2]: https://www.redhat.com/sysadmin/
|
||||
[3]: https://www.audacityteam.org/
|
||||
[4]: https://opensource.com/article/17/12/using-sudo-delegate
|
||||
[5]: https://opensource.com/education/16/9/audacity-classroom
|
||||
[6]: https://manual.audacityteam.org/man/installing_and_updating_audacity_on_linux.html#linlame
|
||||
[7]: https://manual.audacityteam.org/man/installing_and_updating_audacity_on_linux.html#linff
|
||||
[8]: https://opensource.com/sites/default/files/uploads/audacity1_tracksloaded.png (Tracks loaded in Audacity)
|
||||
[9]: https://opensource.com/sites/default/files/uploads/audacity2_tools.png (Audacity toolbox)
|
||||
[10]: https://opensource.com/sites/default/files/uploads/audacity3_audioclips.png (Audio clips in Audacity)
|
||||
[11]: https://opensource.com/sites/default/files/uploads/audacity4_edittoolbar.png (Audacity edit tools)
|
||||
[12]: https://opensource.com/sites/default/files/uploads/audacity5_trackmovement.png (Moving tracks in Audacity)
|
||||
[13]: https://manual.audacityteam.org/#
|
||||
@@ -1,77 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Linux and Kubernetes: Serving The Common Goals of Enterprises)
|
||||
[#]: via: (https://www.linux.com/articles/linux-and-kubernetes-serving-the-common-goals-of-enterprises/)
|
||||
[#]: author: (Swapnil Bhartiya https://www.linux.com/author/swapnil/)
|
||||
|
||||
Linux and Kubernetes: Serving The Common Goals of Enterprises
|
||||
======
|
||||
|
||||
[![][1]][2]
|
||||
|
||||
For [Stefanie Chiras,][3] VP & GM, Red Hat Enterprise Linux (RHEL) Business Unit at [Red Hat][4], aspects such as security and resiliency have always been important for Red Hat. More so, in the current situation when everyone has gone fully remote and it’s much harder to get people in front of the hardware for carrying out updates, patching, etc.
|
||||
|
||||
“As we look at our current situation, never has it been more important to have an operating system that is resilient and secure, and we’re focused on that,” she said.
|
||||
|
||||
The recently released version of [Red Hat Enterprise Linux (RHEL) 8.2][5] inadvertently address these challenge as it makes it easier for technology leaders to embrace the latest, production-ready innovations swiftly which offering security and resilience that their IT teams need.
|
||||
|
||||
RHEL’s embrace of a predictable 6-month minor release cycle also helped customers plan upgrades more efficiently.
|
||||
|
||||
“There is value for customers in having predictability of minor releases on a six-month cycle. Without knowing when they were coming was causing disruptions for them. The launch of 8.2 is now the second time we have delivered on our commitment of having minor releases every six months,” said Stefanie Chiras.
|
||||
|
||||
In addition to offering security updates, the new version adds insights capabilities and forays into newer areas of innovation.
|
||||
|
||||
The upgrade has expanded the earlier capability called ‘Adviser’ dramatically. Additional functionalities such as drift monitoring and CVE coverage allow for a much deeper granularity into how the infrastructure is running.
|
||||
|
||||
“It really amplifies the skills that are already present in ops and sysadmin teams, and this provides a Red Hat consultation, if you will, directly into the data center,” claimed Charis.
|
||||
|
||||
As containers are increasingly being leveraged for digital transformation, RHEL 8.2 offers an updated application stream of Red Hat’s container tools. It also has new, containerized versions of Buildah and Skopeo.
|
||||
|
||||
[Skopeo][6] is an open-source image copying tool, while Buildah is a tool for building Docker- and Kubernetes-compatible images easily and quickly.
|
||||
|
||||
RHEL has also ensured in-place upgrades in the new version. Customers can now directly in-place upgrade from version 7 to version 8.2.
|
||||
|
||||
Chiras believes Linux has emerged as the go-to-platform for innovations such as Machine Learning, Deep Learning, and Artificial Intelligence.
|
||||
|
||||
“Linux has now become the springboard of innovation,” she argued. “AI, machine learning, and deep learning are driving a real change in not just the software but also the hardware. In the context of these emerging technologies, it’s all about making them consumable into an enterprise.”
|
||||
|
||||
“We’re very focused on our ecosystem, making sure that we’re working in the right upstream communities with the right ISVs, with the right hardware partners to make all of that magic come together,” Chiras said.
|
||||
|
||||
Towards this end, Red Hat has been partnering with multiple architectures for a long time — be it an x86 architecture, ARM, Power, or mainframe with IBM Z. Its partnership with Nvidia pulls in capabilities such as FPGAs, and GPU.
|
||||
|
||||
**Synergizing Kubernetes and Linux **
|
||||
|
||||
Kubernetes is fast finding favor in enterprises. So how do Linux and Kubernetes serve the common goals of enterprises?
|
||||
|
||||
“Kubernetes is a new way to deploy Linux. We’re very focused on providing operational consistency by leveraging our technology in RHEL and then bringing in that incredible capability of Kubernetes within our OpenShift product line,” Chiras said.
|
||||
|
||||
The deployment of Linux within a Kubernetes environment is much more complicated than in a traditional deployment. RHEL, therefore, made some key changes. The company created Red Hat Enterprise Linux CoreOS — an optimized version of RHEL for the OpenShift experience.
|
||||
|
||||
“It’s deployed as an immutable. It’s tailored, narrow, and gets updated as part of your OpenShift update to provide consistent user experience and comprehensive security.
|
||||
|
||||
The launch of the Red Hat Universal Base Image (UBI) offers users greater security, reliability, and performance of official Red Hat container images where OCI-compliant Linux containers run.
|
||||
|
||||
“Kubernetes is a new way to deploy Linux. It really is a tight collaboration but what we’re really focused on is the customer experience. We want them to get easy updates with consistency and reliability, resilience and security. We’re pulling all of that together. With such advancements going on, it’s a fascinating space to watch,” added Chiras.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/articles/linux-and-kubernetes-serving-the-common-goals-of-enterprises/
|
||||
|
||||
作者:[Swapnil Bhartiya][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.linux.com/author/swapnil/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.linux.com/wp-content/uploads/2019/12/computer-2930704_1280-1068x634.jpg (computer-2930704_1280)
|
||||
[2]: https://www.linux.com/wp-content/uploads/2019/12/computer-2930704_1280.jpg
|
||||
[3]: https://www.linkedin.com/in/stefanie-chiras-9022144/
|
||||
[4]: https://www.redhat.com/en
|
||||
[5]: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html-single/8.2_release_notes/index
|
||||
[6]: https://github.com/containers/skopeo
|
||||
@@ -1,123 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Transparent, open source alternative to Google Analytics)
|
||||
[#]: via: (https://opensource.com/article/20/5/plausible-analytics)
|
||||
[#]: author: (Marko Saric https://opensource.com/users/markosaric)
|
||||
|
||||
Transparent, open source alternative to Google Analytics
|
||||
======
|
||||
Plausible Analytics is a leaner, more transparent option, with the
|
||||
essential data you need but without all the privacy baggage.
|
||||
![Digital creative of a browser on the internet][1]
|
||||
|
||||
Google Analytics is the most popular website analytics tool. Millions of developers and creators turn to it to collect and analyze their website statistics.
|
||||
|
||||
More than 53% of all sites on the web track their visitors using Google Analytics. [84%][2] of sites that do use a known analytics script use Google Analytics.
|
||||
|
||||
Google Analytics has, for years, been one of the first tools I installed on a newly launched site. It is a powerful and useful analytics tool. Installing Google Analytics was a habit I didn't think much about until the introduction of the [GDPR][3] (General Data Protection Regulation) and other privacy regulations.
|
||||
|
||||
Using Google Analytics these days comes with several pitfalls, including the need for a privacy policy, the need for cookie banners, and the need for a GDPR consent prompt. All these may negatively impact the site loading time and visitor experience.
|
||||
|
||||
This has made me try to [de-Google-ify websites][4] that I work on, and it's made me start working on independent solutions that are open source and more privacy-friendly. This is where Plausible Analytics enters the story.
|
||||
|
||||
[Plausible Analytics][5] is an open source and lightweight alternative to Google Analytics. It doesn't use cookies and it doesn't collect any personal data, so you don't need to show any cookie banners or get GDPR or CCPA consent. Let's take a closer look.
|
||||
|
||||
### Main differences between Google Analytics and Plausible
|
||||
|
||||
Plausible Analytics is not designed to be a clone of Google Analytics. It is meant as a simple-to-use replacement and a privacy-friendly alternative. Here are the main differences between the two web analytics tools:
|
||||
|
||||
#### Open source vs. closed source
|
||||
|
||||
Google Analytics may be powerful and useful, but it is closed source. It is a proprietary tool run by one of the largest companies in the world, a company that is a key player in the ad-tech industry. There's simply no way of knowing what's going on behind the scenes. You have to put your trust in Google.
|
||||
|
||||
Plausible is a fully open source tool. You can read our code [on GitHub][6]. We're "open" in other ways, too, such as our [public roadmap][7], which is based around the feedback and features submitted by the members of our community.
|
||||
|
||||
#### Privacy of your website visitors
|
||||
|
||||
Google Analytics places [several cookies][8] on the devices of your visitors, and it tracks and collects a lot of data. This means that there are several requirements if you want to use Google Analytics and be compliant with the different regulations:
|
||||
|
||||
* You need to have a privacy policy about analytics
|
||||
* You need to show a cookie banner
|
||||
* You need to obtain a GDPR/CCPA consent
|
||||
|
||||
|
||||
|
||||
Plausible is made to be fully compliant with the privacy regulations. No cookies are used, and no personal data is collected. This means that you don't need to display the cookie banner, you don't need a privacy policy, and you don't need to ask for the GDPR/CCPA consent when using Plausible.
|
||||
|
||||
#### Page weight and loading time
|
||||
|
||||
The recommended way of installing Google Analytics is to use the Google Tag Manager. Google Tag Manager script weights 28 KB, and it downloads another JavaScript file called the Google Analytics tag, which adds an additional 17.7 KB to your page size. That's 45.7 KB of page weight combined.
|
||||
|
||||
Plausible script weights only 1.4 KB. That's 33 times smaller than the Google Analytics Global Site Tag. Every KB matters when you want to keep your site fast to load.
|
||||
|
||||
#### Accuracy of visitor stats
|
||||
|
||||
Google Analytics is being blocked by an increasing number of web users. It's blocked by those who use open source browsers such as [Firefox][9] and [Brave][10]. It's also blocked by those who use open source browser add-ons such as the [uBlock Origin][11]. It's not uncommon to see 40% or more of the audience on a tech site blocking Google Analytics.
|
||||
|
||||
Plausible is a new player on this market and it's privacy-friendly by default, so it doesn't see the same level of blockage.
|
||||
|
||||
#### Simple vs. complex web analytics
|
||||
|
||||
[Google Analytics is overkill][12] for many website owners. It's a complex tool that takes time to understand and requires training. Google Analytics presents hundreds of different reports and metrics for you to get insights from. Many users end up creating custom dashboards while ignoring all the rest.
|
||||
|
||||
Plausible cuts through all the noise that Google Analytics creates. It presents everything you need to know on one single page—all the most valuable metrics at a glance. You can get an overview of the most actionable insights about your website in one minute.
|
||||
|
||||
### A guided tour of Plausible Analytics
|
||||
|
||||
Plausible Analytics is not a full-blown replacement and a feature-by-feature reproduction of Google Analytics. It's not designed for all the different use-cases of Google Analytics.
|
||||
|
||||
It's built with simplicity and speed in mind. There is no navigational menu. There are no additional sub-menus. There is no need to create custom reports. You get one simple and useful web analytics dashboard out of the box.
|
||||
|
||||
Rather than tracking every metric imaginable, many of them that you will never find a use for, Plausible focuses on the essential website stats only. It is easy to use and understand with no training or prior experience:
|
||||
|
||||
![Plausible analytics in action][13]
|
||||
|
||||
* Choose the time range that you want to analyze. The visitor numbers are automatically presented on an hourly, daily, or monthly graph. The default time frame is set at the last 30 days.
|
||||
* See the number of unique visitors, total page views, and the bounce rate. These metrics include a percentage comparison to the previous time period, so you understand if the trends are going up or down.
|
||||
* See all the referral sources of traffic and all the most visited pages on your site. Bounce rates of the individual referrals and pages are included too.
|
||||
* See the list of countries your traffic is coming from. You can also see the device, browser, and operating system your visitors are using.
|
||||
* Track events and goals to identify the number of converted visitors, the conversion rate, and the referral sites that send the best quality traffic.
|
||||
|
||||
|
||||
|
||||
Take a look at the [live demo][14] where you can follow the traffic to the Plausible website.
|
||||
|
||||
### Give Plausible Analytics a chance
|
||||
|
||||
With Plausible Analytics, you get all the important web analytics at a glance so you can focus on creating a better site without needing to annoy your visitors with all the different banners and prompts.
|
||||
|
||||
You can try Plausible Analytics on your site alongside Google Analytics. [Register today][15] to try it out, and see what you like and what you don't. Share your feedback with the community. This helps us learn and improve. We'd love to hear from you.
|
||||
|
||||
Take a look at five great open source alternatives to Google Docs.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/5/plausible-analytics
|
||||
|
||||
作者:[Marko Saric][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/markosaric
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/browser_web_internet_website.png?itok=g5B_Bw62 (Digital creative of a browser on the internet)
|
||||
[2]: https://w3techs.com/technologies/details/ta-googleanalytics
|
||||
[3]: https://gdpr-info.eu/
|
||||
[4]: https://markosaric.com/degoogleify/
|
||||
[5]: https://plausible.io/
|
||||
[6]: https://github.com/plausible-insights/plausible
|
||||
[7]: https://feedback.plausible.io/roadmap
|
||||
[8]: https://developers.google.com/analytics/devguides/collection/analyticsjs/cookie-usage
|
||||
[9]: https://www.mozilla.org/en-US/firefox/new/
|
||||
[10]: https://brave.com/
|
||||
[11]: https://github.com/gorhill/uBlock
|
||||
[12]: https://plausible.io/vs-google-analytics
|
||||
[13]: https://opensource.com/sites/default/files/plausible-analytics.png (Plausible analytics in action)
|
||||
[14]: https://plausible.io/plausible.io
|
||||
[15]: https://plausible.io/register
|
||||
@@ -1,127 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (13 tips for getting your talk accepted at a tech conference)
|
||||
[#]: via: (https://opensource.com/article/20/5/tips-conference-proposals)
|
||||
[#]: author: (Todd Lewis https://opensource.com/users/toddlewis)
|
||||
|
||||
13 tips for getting your talk accepted at a tech conference
|
||||
======
|
||||
Before you respond to an event's call for papers, make sure your talk's
|
||||
proposal aligns with these best practices.
|
||||
![All Things Open check-in at registration booth][1]
|
||||
|
||||
As tech conference organizers ramp up for the fall season, you may be seeing calls for papers (CFP) landing in your email box or social media feeds. We at [All Things Open][2] (ATO) have seen a lot of presentation proposals over the years, and we've learned a few things about what makes them successful.
|
||||
|
||||
As we prepare for the eighth annual ATO in October 2020, we thought we'd offer a few best practices for writing successful CFP responses. If you're considering submitting a talk to ATO or another tech event, we hope these tips will help improve the chances that your proposal will be accepted.
|
||||
|
||||
### 1\. Know the event you're submitting a talk to
|
||||
|
||||
This seems like the proverbial _no-brainer_, but some people don't take the time to research an event before they submit a talk. Peruse the conference's website and review the talks, speakers, topics, etc. featured in the last couple of years. You can also find a lot of information simply by googling. The time you invest here will help you avoid a submission that is completely out of context for the event.
|
||||
|
||||
### 2\. Understand what the event is looking for
|
||||
|
||||
Look for information about what the event is looking for and what types of topics or talks it expects will be a good fit. We try to provide as much information as possible about the [ATO conference][3], [why someone would want to speak][4], and [what we're looking for][5] (both general and special interest topics). We also try to make the submission process as easy as possible (no doubt, there is room for improvement), in part because we believe this improves the quality of submissions and makes our review process go more smoothly.
|
||||
|
||||
### 3\. Reach out to the organizer and ask questions
|
||||
|
||||
If you're considering submitting a talk, don't hesitate to reach out and ask the event organizers any questions you have and for guidance specific to the event. If there is no or little response, that should be a red flag. If you have any questions about All Things Open, please reach out directly at [info@allthingsopen.org][6].
|
||||
|
||||
### 4\. Be clear about what attendees will learn from your talk
|
||||
|
||||
This is one of the most common mistakes we see. Only about 25% of the proposals we receive clearly explain the proposed talk's takeaways. One reason you should include this is that nearly every event attendee makes their schedule based on what they will learn if they go to a session. But for organizers and proposal reviewers, having this information clearly stated upfront is pure gold. It simplifies and speeds up the assessment process, which gets you one step closer to being accepted as a speaker. A paragraph titled "Attendee Takeaways" with bullet points is the holy grail for everyone involved.
|
||||
|
||||
### 5\. Keep recommended word counts in mind
|
||||
|
||||
This is another mistake we see a lot. Many talks are submitted with either a single sentence description in the abstract or an extraordinary long volume of text. Neither is a good idea. The only exception we can think of is when a topic is very popular or topical, and that alone is enough to win the day even if the abstract is extremely short (but this is rare). Most abstracts should be between 75 and 250 words, and perhaps more for an extended workshop with prerequisites (e.g., preexisting knowledge or required downloads). Even then, try to keep your proposal as sharp, concise, and on-point as possible.
|
||||
|
||||
Disregard this advice at your own risk; otherwise, there's a high likelihood that your proposal will be met with one of these reactions from reviewers: "They didn't take the time to write any more than this?" or "Sheesh, there's no way I have the time to read all that. I'm going to give it the lowest score and move on."
|
||||
|
||||
### 6\. Choose a good title
|
||||
|
||||
This is a debate we see all the time: Should a talk's title describe what the talk is about, or should it be written to stand out and get attention (e.g., evoking emotion, anchoring to a popular pop culture topic, or asking a compelling question)? There isn't a single correct answer to this question, but we definitely know when a title "works" and when it doesn't. We've seen some very creative titles work well and generate interest, and we've seen very straightforward titles work well, also.
|
||||
|
||||
Here is our rule of thumb: If the talk covers a topic that has been around a while and is not particularly _hot_ right now, try getting creative and spicing it up a bit. If the topic is newer, a more straightforward title describing the talk in plain terms should be good.
|
||||
|
||||
Titles on an event schedule may be the only thing attendees use to decide what talks to attend. So, run your potential talk titles by colleagues and friends, and seek their opinions. Ask: "If you were attending an event and saw this title on the schedule, would it pique your interest?"
|
||||
|
||||
### 7\. Know the basic criteria that reviewers and organizers use to make decisions
|
||||
|
||||
While this isn't a comprehensive list of review criteria, most reviewers and organizers consider one or more of the following when evaluating talk proposals. Therefore, at minimum, consider this list when you're creating a talk and the components that go with it.
|
||||
|
||||
1. **Timeliness of and estimated interest in the topic:** Is the topic applicable to the session's target audience? Will it deliver value? Is it timely?
|
||||
2. **Educational value:** Based on the abstract and speaker, is it clear that attendees will learn something from the talk? As mentioned in item 4 above, including an "Attendee Takeaways" section is really helpful to establish educational value.
|
||||
3. **Technical value:** Is the technology you intend to showcase applicable, unique, or being used in a new and creative way? Is there a live demo or a hands-on component? While some topics don't lend themselves to a demo, most people are visual learners and are better off if a presentation includes one (if it's relevant). For this reason, we place a lot of value on demos and hands-on content.
|
||||
4. **Diversity:** Yes, there are exceptions, but the majority of events, reviewers, and organizers agree that having a diverse speaker lineup is optimal and results in a better overall event in multiple ways. A topic delivered from a different perspective can often lead to creative breakthroughs for attendees, which is a huge value-add. See item 10 below for more on this.
|
||||
5. **Talk difficulty level:** We identify All Things Open talks as introductory, intermediate, or advanced. Having a good mix of talk levels ensures everyone in attendance can access applicable content. See item 9 below for more on this, but in general, it's smart to indicate your talk's level, whether or not the CFP requests it.
|
||||
|
||||
|
||||
|
||||
### 8\. Stay current on the event's industry or sector
|
||||
|
||||
Submitting a proposal on a relevant topic increases the probability your talk will be accepted. But how do you know what topics are of interest, especially if the CFP doesn't spell it out in simple terms? The best way to know what's timely and interesting is to deeply understand the sector the event focuses on.
|
||||
|
||||
Yes, this requires time and effort, and it implies you enjoy the sector enough to stay current on it, but it will pay off. This knowledge will result in a higher _sector IQ_, which will be reflected in your topic, title, and abstract. It will be recognized by reviewers and immediately set you apart from others. At All Things Open, we spend the majority of our time reading about and staying current on the "open" space so that we can feature relevant, substantive, and informed content. Submitting a talk that is relevant, substantive, and informed greatly enhances the chance it will be accepted.
|
||||
|
||||
### 9\. Describe whether the talk is introductory, intermediate, or advanced
|
||||
|
||||
Some CFPs don't ask for this information, but you should offer it anyway. It will make the reviewers and organizer very happy for multiple reasons, including these:
|
||||
|
||||
1. Unless the event targets attendees with a certain skill or experience level (and most do not), organizers must include content that is appealing to a wide audience, including people of all skill, experience, and expertise levels. Even if an event focuses on a specific type of attendee (perhaps people with higher levels of experience or skills), most want to offer something a little different. Listing the talk level makes this much easier for organizers.
|
||||
2. News flash: Reviewers and organizers don't know everything and are not experts in every possible topic area. As a result, reviewers will sometimes look for a few keywords or other criteria, and adding the talk level can "seal the deal" and get your talk confirmed.
|
||||
|
||||
|
||||
|
||||
### 10\. Tell organizers if you're a member of a historically underrepresented group
|
||||
|
||||
A growing number of events are getting better at recognizing the value of diversity and ensuring their speaker lineup reflects it. If you're part of a group that hasn't typically been included in tech events and leadership, look to see if there is a place to indicate that on the submission form. If not, mention it in a conspicuous place somewhere in the abstract. This does not guarantee approval in any way—your proposal must still be well-written and relevant—but it does give reviewers and organizers pertinent information they may value and take into consideration.
|
||||
|
||||
### 11\. Don't be ashamed of your credentials or speaking experience if it is light
|
||||
|
||||
We talk to a lot of people who would like to deliver a presentation and have a lot to offer, but they never submit a talk because they don't feel they're qualified to speak. _Not true._ Some of the best talks we've seen are from first-time speakers or those very early in their speaking careers. Go ahead and submit the talk, and be honest when discussing your background. Most reviewers and organizers will focus on the substance of the submission over your experience and recognize that new ways of approaching and using technology often come from newbies rather than industry veterans.
|
||||
|
||||
One caveat here: It still pays to know yourself. By this, we mean if you absolutely hate public speaking, have no desire to do it, and are only considering submitting a talk due to, for example, pressure from an employer, the talk is not likely to go well. It's better, to be honest, on the frontend than force something you have no desire to do.
|
||||
|
||||
### 12\. Consider panel sessions carefully
|
||||
|
||||
If you've got an idea for a panel session, please consider it carefully. In more than 10 years of hosting events we've seen some really good panel sessions, but we've seen far more that didn't go so well. Perhaps too many people were on the panel and not everyone had a chance to speak, perhaps a single panel member dominated the entire conversation, or perhaps the moderator didn't keep the dialogue and engagement flowing smoothly. Regardless of the issue, panels have the potential to go very wrong.
|
||||
|
||||
That said, panels can still work and deliver a lot of value to attendees. If you do submit a panel session be sure to keep in mind the amount of time allotted for the session and confirm the number of panel members accordingly. Remember, less is always more when it comes to the panel format. Also, be sure the moderator understands the subject matter being discussed and doesn't mind enforcing format parameters and speaking time limits. Finally, let organizers know panel members and the moderator will engage in a pre-conference walk-through/preparation call before the event to ensure a smooth process in front of a live audience. Remember, organizers are well aware panels can be terrific but can also go in the opposite direction and very easily lead to a lot of negative feedback.
|
||||
|
||||
### 13\. This is not an opportunity to sell
|
||||
|
||||
This is a sensitive topic, but one that absolutely must be mentioned. Over the years we've seen literally hundreds of talks "disqualified" by reviewers because they viewed the talk as a sales pitch. Few things evoke such a visceral response. Yes, there are events, tracks, and session slots where a sales pitch is appropriate (and maybe even required by the company paying your costs). However, make it a priority to know when and where this is appropriate and acceptable. And always, and we mean always, err on the side of making substance the focus of the talk rather than a sales angle.
|
||||
|
||||
It might sound like a cliche, but when a talk is delivered effectively with a focus on substance, people will **want** to buy what you're selling. And if you're not selling anything, they'll want to follow you on social media and generally engage with you—because you delivered value to them. Meaning: You gave them something they can apply themselves (education) or because your delivery style was entertaining and engaging. With rare exceptions, always focus any abstract on substance, and the rest will take care of itself.
|
||||
|
||||
### Go for it!
|
||||
|
||||
We greatly admire and respect anyone who submits a talk for consideration—it takes a lot of time, thought, and courage. Therefore, we go to great lengths to thank everyone who goes through the process; we give free event passes to everyone who applies (regardless of approval or rejection), and we make every effort to host Q&A sessions to provide as much guidance as possible on the front end. Again, the more time and consideration speakers put into the submission process, the easier the lives of reviewers and organizers. We need to make all of this as easy as possible.
|
||||
|
||||
While this is not a comprehensive list of best practices, it includes some of the things we think people can benefit from knowing before submitting a talk. There are a lot of people out there with more knowledge and experience, so please share your best tips for submitting conference proposals in the comments, so we can all learn from you.
|
||||
|
||||
* * *
|
||||
|
||||
_[All Things Open][2] is a universe of platforms and events focusing on open source, open tech, and the open web. It hosts the [All Things Open conference][3], the largest open source/tech/web event on the US East Coast. The conference regularly hosts thousands of attendees and many of the world's most influential companies from a wide variety of industries and sectors. In 2019, nearly 5,000 people attended from 41 US states and 24 countries. Please direct inquiries about ATO to the team at [info@allthingsopen.org][6]._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/5/tips-conference-proposals
|
||||
|
||||
作者:[Todd Lewis][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/toddlewis
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/ato2016_checkin_conference.jpg?itok=DJtoSS6t (All Things Open check-in at registration booth)
|
||||
[2]: https://www.allthingsopen.org/
|
||||
[3]: https://2020.allthingsopen.org/
|
||||
[4]: https://2020.allthingsopen.org/call-for-speakers
|
||||
[5]: https://www.allthingsopen.org/what-were-looking-for/
|
||||
[6]: mailto:info@allthingsopen.org
|
||||
@@ -1,133 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Analyzing data science code with R and Emacs)
|
||||
[#]: via: (https://opensource.com/article/20/5/r-emacs-data-science)
|
||||
[#]: author: (Peter Prevos https://opensource.com/users/danderzei)
|
||||
|
||||
Analyzing data science code with R and Emacs
|
||||
======
|
||||
Emacs' versatility and extensibility bring the editor's full power into
|
||||
play for writing data science code.
|
||||
![metrics and data shown on a computer screen][1]
|
||||
|
||||
Way back in 2012, _Harvard Business Review_ published an article that proclaimed "data scientist" to be the [sexiest job][2] of the 21st century. Interest in data science has exploded since then. Many great open source projects, such as [Python][3] and the [R language][4] for statistical computing, have facilitated the rapid developments in how we analyze data.
|
||||
|
||||
I started my career using pencil and paper and moved to spreadsheets. Now the R language is my weapon of choice when I need to create value from data. Emacs is another one of my favorite tools. This article briefly explains how to use the [Emacs Speaks Statistics][5] (ESS) package to get started with developing R projects in this venerable editor.
|
||||
|
||||
The vast majority of R developers use the [RStudio][6] IDE to manage their projects. RStudio is a powerful open source editor with specialized functionality to develop data science projects. RStudio is a great integrated development environment (IDE), but its editing functions are limited.
|
||||
|
||||
Using Emacs to write data science code means that you have access to the full power of this extensible editor. I prefer using Emacs for my data science projects because I can do many other tasks within the same application, leveraging the multifunctionality of this venerable editor. If you are just getting started with Emacs, then please first read Seth Kenlon's [Emacs getting started][7] article.
|
||||
|
||||
### Setting up Emacs for R
|
||||
|
||||
Emacs is an almost infinitely extensible text editor, which unfortunately means that many things don't work the way you want them to out of the box. Before you can write and execute R scripts, you need to install some packages and configure them. The ESS package provides an interface between Emacs and R. Other packages, such as [Company][8] and [highlight-parentheses][9] help with completion and balancing parentheses.
|
||||
|
||||
Emacs uses a version of Lisp for configuration. The lines of [Emacs Lisp][10] code below install the required extensions and define a minimal configuration to get you started. These lines were tested for GNU Emacs version 26.3.
|
||||
|
||||
Copy these lines and save them in a file named **init.el** in your **.emacs.d** folder. This is the folder that Emacs uses to store configurations, including the [init file][11]. If you already have an init file, then you can append these lines to your config. This minimal configuration is enough to get you started.
|
||||
|
||||
|
||||
```
|
||||
;; Elisp file for R coding with Emacs
|
||||
|
||||
;; Add MELPA repository and initialise the package manager
|
||||
(require 'package)
|
||||
(add-to-list 'package-archives
|
||||
'("melpa" . "<https://melpa.org/packages/>"))
|
||||
(package-initialize)
|
||||
|
||||
;; Install use-package,in case it does not exist yet
|
||||
;; The use-package software will install all other packages as required
|
||||
(unless (package-installed-p 'use-package)
|
||||
(package-refresh-contents)
|
||||
(package-install 'use-package))
|
||||
|
||||
;; ESS configurationEmacs Speaks Statistics
|
||||
(use-package ess
|
||||
:ensure t
|
||||
)
|
||||
|
||||
;; Auto completion
|
||||
(use-package company
|
||||
:ensure t
|
||||
:config
|
||||
(setq company-idle-delay 0)
|
||||
(setq company-minimum-prefix-length 2)
|
||||
(global-company-mode t)
|
||||
)
|
||||
|
||||
; Parentheses
|
||||
(use-package highlight-parentheses
|
||||
:ensure t
|
||||
:config
|
||||
(progn
|
||||
(highlight-parentheses-mode)
|
||||
(global-highlight-parentheses-mode))
|
||||
)
|
||||
```
|
||||
|
||||
### Using the R console
|
||||
|
||||
To start an R console session, press **M-x R** and hit **Enter** (**M** is the Emacs way to denote the **Alt** or **Command** key). ESS will ask you to nominate a working directory, which defaults to the folder of the current buffer. You can use more than one console in the same Emacs session by repeating the R command.
|
||||
|
||||
Emacs opens a new buffer for your new R console. You can also use the **Up** and **Down** arrow keys to go to previous lines and re-run them. Use the **Ctrl** and **Up/Down** arrow keys to recycle old commands.
|
||||
|
||||
The Company ("complete anything") package manages autocompletion in both the console and R scripts. When entering a function, the mini-buffer at the bottom of the screen shows the relevant parameters. When the autocompletion dropdown menu appears, you can press **F1** to view the chosen option's Help file before you select it.
|
||||
|
||||
The [highlight-parentheses][9] package does what its name suggests. Several other Emacs packages are available to help you balance parentheses and other structural elements in your code.
|
||||
|
||||
### Writing R scripts
|
||||
|
||||
Emacs recognizes R mode for any buffer with a **.R** extension (the file extension is case-sensitive). Open or create a new file with the **C-x C-f** shortcut and type the path and file name. You can start writing your code and use all of the powerful editing techniques that Emacs provides.
|
||||
|
||||
Several functions are available to evaluate the code. You can evaluate each line separately with **C-<return>**, while **C-c C-c** will evaluate a contiguous region. Keying **C-c C-b** will evaluate the whole buffer.
|
||||
|
||||
When you evaluate some code, Emacs will use any running console or ask you to open a new console to run the code.
|
||||
|
||||
The output of any plotting functions appears in a window outside of Emacs. If you prefer to view the output within Emacs, then you need to save the output to disk and open the resulting file in a separate buffer.
|
||||
|
||||
![Literate programming in Org mode, the ESS buffer, and graphics output.][12]
|
||||
|
||||
Literate programming in Org mode, the ESS buffer, and graphics output.
|
||||
|
||||
### Advanced use
|
||||
|
||||
This article provides a brief introduction to using R in Emacs. Many parameters can be fine-tuned to make Emacs behave according to your preferences, but it would take too much space to cover them here. The [ESS manual][13] describes these in detail. You can also extend functionality with additional packages.
|
||||
|
||||
Org mode can integrate R code, providing a productive platform for literate programming. If you prefer to use RMarkdown, the [Polymode][14] package has you covered.
|
||||
|
||||
Emacs has various packages to make your editing experience more efficient. The best part of using Emacs to write R code is that the program is more than just an IDE; it is a malleable computer system that you can configure to match your favorite workflow.
|
||||
|
||||
Learning how to configure Emacs can be daunting. The best way to learn quickly is to copy ideas from people who share their configurations. Miles McBain manages a [list of Emacs configurations][15] that could be useful if you want to explore using the R language in Emacs further.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/5/r-emacs-data-science
|
||||
|
||||
作者:[Peter Prevos][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/danderzei
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/metrics_data_dashboard_system_computer_analytics.png?itok=oxAeIEI- (metrics and data shown on a computer screen)
|
||||
[2]: https://hbr.org/2012/10/data-scientist-the-sexiest-job-of-the-21st-century
|
||||
[3]: https://www.python.org/
|
||||
[4]: https://www.r-project.org/
|
||||
[5]: https://ess.r-project.org/
|
||||
[6]: https://opensource.com/article/18/2/getting-started-RStudio-IDE
|
||||
[7]: https://opensource.com/article/20/3/getting-started-emacs
|
||||
[8]: https://company-mode.github.io/
|
||||
[9]: https://github.com/tsdh/highlight-parentheses.el
|
||||
[10]: https://en.wikipedia.org/wiki/Emacs_Lisp
|
||||
[11]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Init-File.html
|
||||
[12]: https://opensource.com/sites/default/files/uploads/r-ess-screenshot.jpg (Literate programming in Org mode, the ESS buffer, and graphics output.)
|
||||
[13]: https://ess.r-project.org/index.php?Section=documentation&subSection=manuals
|
||||
[14]: https://github.com/polymode/polymode
|
||||
[15]: https://github.com/MilesMcBain/esscss
|
||||
@@ -1,182 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Metaphors in man pages)
|
||||
[#]: via: (https://jvns.ca/blog/2020/05/08/metaphors-in-man-pages/)
|
||||
[#]: author: (Julia Evans https://jvns.ca/)
|
||||
|
||||
Metaphors in man pages
|
||||
======
|
||||
|
||||
This morning I was watching a [great talk by Maggie Appleton][1] about metaphors. In the talk, she explains the difference between a “figurative metaphor” and a “cognitive metaphor”, and references this super interesting book called [Metaphors We Live By][2] which I immediately got and started reading.
|
||||
|
||||
Here’s an example from “Metaphors We Live By” of a bunch of metaphors we use for ideas:
|
||||
|
||||
* ideas as **food**: “_raw_ facts”, “_half-baked_ ideas”, “_swallow_ that claim”, “_spoon-feed_ our students”, “_meaty_ part of the paper”, “that idea has been _fermenting_ for years”
|
||||
* ideas as **people**: “the theory of relativity _gave birth_ to an enormous number of ideas”, “whose _brainchild_ was that”, “those ideas _died off_ in the middle ages”, “cognitive psychology is in its _infancy_“
|
||||
* ideas as **products**: “we’ve _generated_ a lot of ideas this week”, “it needs to be _refined_”, “his _intellectual productivity_ has decreased in recent years”
|
||||
* ideas as **commodities**: “he won’t _buy_ that”, “that’s a _worthless_ idea”, “she has _valuable_ ideas”
|
||||
* ideas as **resources**: “he _ran out_ of ideas”, “let’s _pool_ our ideas”, “that idea will _go a long way_“
|
||||
* ideas as **cutting instruments**: “that’s an _incisive_ idea”, “that _cuts right to the heart_ of the matter”, “he’s _sharp_“
|
||||
* ideas as **fashions**: “that idea _went out of style_ years ago”, “marxism is _fashionable_ in western europe”, “berkeley is a center of _avant-garde_ thought”, “semiotics has become quite _chic_“
|
||||
|
||||
|
||||
|
||||
There’s a [long list of more English metaphors here][3], including many metaphors from the book.
|
||||
|
||||
I was surprised that there were so many different metaphors for ideas, and that we’re using metaphors like this all the time in normal language.
|
||||
|
||||
### let’s look for metaphors in man pages!
|
||||
|
||||
Okay, let’s get to the point of this blog post, which is just a small fun exploration – there aren’t going to be any Deep Programming Insights here.
|
||||
|
||||
I went through some of the examples of metaphors in Metaphors To Live By and grepped all the man pages on my computer for them.
|
||||
|
||||
### processes as people
|
||||
|
||||
This is one of the richer categories – a lot of different man pages seem to agree that processes are people, or at least alive in some way.
|
||||
|
||||
* Hangup detected on controlling terminal or **death** of controlling process (`man 7 signal`)
|
||||
* can access the local **agent** through the forwarded connection (`man ssh_config`)
|
||||
* If the exit of the process causes a process group to become **orphaned** (`man exit`)
|
||||
* If a parent process terminates, then its **“zombie” children** (if any) (`man wait`)
|
||||
* … send SIGHUP to the **parent** process of the client (`man tmux`)
|
||||
* Otherwise, it **“runs” to catch up** or waits (`man mplayer`)
|
||||
* However, Git does not (and it should not) change tags **behind users back** (`man git-tag`)
|
||||
* will **listen** forever for a connection (`man nc_openbsd`)
|
||||
* this monitor scales badly with the number of files being **observed** (`man fswatch`)
|
||||
* If you try to use the **birth** time of a reference file (`man file`)
|
||||
* a program **died** due to a fatal signal (`man xargs`)
|
||||
* protocol version in the TLS **handshake** (`man curl`)
|
||||
* it will **look for** a debug object at… (`man valgrind`)
|
||||
|
||||
|
||||
|
||||
### data as food
|
||||
|
||||
* “Apparently some digital cameras get **indigestion** if you feed them a CF card) (`man mkfs`)
|
||||
* “Send packets using **raw** ethernet frames or IP packets” (`man nmap`)
|
||||
* “the above example can be thought of as a maximizing repeat that must **swallow** everything it can” (`man pcrepattern`)
|
||||
* “This will allow you to **feed** newline-delimited name=value pairs to the script on’ (`man CGI`)
|
||||
|
||||
|
||||
|
||||
### data as objects
|
||||
|
||||
* Kill the tmux server and clients and **destroy** all sessions (`tmux`)
|
||||
* Each command will produce one **block** of output on standard output. (`man tmux`)
|
||||
* “HTTPS guarantees that the password will not **travel** in the clear” (`man Net::SSLeay`)
|
||||
* “way to **pack** more than one certificate into an ASN.1 structure” (`man gpgsm`)
|
||||
|
||||
|
||||
|
||||
### processes as machines/objects
|
||||
|
||||
* “This is **fragile**, subject to change, and thus should not be relied upon” (`man ps`)
|
||||
* “This is useful if you have to use **broken** DNS” (`man aria2c`)
|
||||
* “This provides good safety measures, but **breaks down** when” (`man git-apply`)
|
||||
* “debugfs is a debugging tool. It has **rough edges**!” (`man debugfs`)
|
||||
|
||||
|
||||
|
||||
### containers
|
||||
|
||||
There are LOTS of containers: directories, files, strings, caches, queues, buffers, etc.
|
||||
|
||||
* can exploit that to **get out** of the chroot directory (`man chroot`)
|
||||
* “The file **containing** the RFC 4648 Section 5 base64url encoded 128-bit secret key”
|
||||
* “Keys must start with a lowercase character and **contain** only hyphens”
|
||||
* “just specify an **empty** string” (`man valgrind`)
|
||||
* “the cache is **full** and a new page that isn’t cached becomes visible” (`man zathurarc`)
|
||||
* “Number of table **overflows**” (`man lnstat`)
|
||||
* “likely **overflow** the buffer” (`man g++`)
|
||||
|
||||
|
||||
|
||||
### resources
|
||||
|
||||
There are also lots of kinds of resources: bandwidth, TCP sockets, session IDs, stack space, memory, disk space.
|
||||
|
||||
* This is not recommended and **wastes** bitrate (`man bitrate`)
|
||||
* corruption or **lost** data if the system crashes (`man btree`)
|
||||
* you don’t want Wget to **consume** the entire available bandwidth (`man wget`)
|
||||
* Larger values will be slower and cause x264 to **consume** more memory (`man mplayer`)
|
||||
* the resulting file can **consume** some disk space (`man socat`)
|
||||
* attempting to **reuse** SSL session-ID (`man curl`)
|
||||
* This option controls stack space **reuse** (`man gcc`)
|
||||
* Keep the TCP socket open between queries and **reuse** it rather than creating a new TCP socket (`man dig`)
|
||||
* the maximum value will easily **eat up** three extra gigabytes or so of memory (`man valgrind`)
|
||||
|
||||
|
||||
|
||||
### orientation (up, down, above, below)
|
||||
|
||||
* Send the escape character to the **frontend** (`man qemu-system`)
|
||||
* Note that TLS 1.3 is only supported by a subset of TLS **backends** (`man curl`)
|
||||
* This option may be useful if you are **behind** a router (`man mplayer`)
|
||||
* When a file that exists on the **lower** layer is renamed (`man rename`)
|
||||
* Several of the socket options should be handled at **lower** levels (`man getsockopt`)
|
||||
* while still performing such **higher** level functionality (`man nmap`)
|
||||
* This is the same string passed **back to** the front end (`man sudo_plugin`)
|
||||
* On Linux, `futimens` is a library function implemented **on top** of the `utimensat` system call (`man futimens`)
|
||||
|
||||
|
||||
|
||||
### buildings
|
||||
|
||||
Limits as rooms/buildings (which have floors, and ceilings, which you hit) are kind of fun:
|
||||
|
||||
* the kernel places a **floor** of 32 pages on this size limit (`man execve`)
|
||||
* This specifies a **ceiling** to which the process’s nice value can be raised (`man getrlimit`)
|
||||
* If this limit is **hit** the search is aborted (`man gcc`)
|
||||
* these libraries are used as the **foundation** for many of the libraries (`man Glib`)
|
||||
|
||||
|
||||
|
||||
### money / wealth
|
||||
|
||||
* This is a very **expensive** operation for large projects, so use it with caution (`man git-log`)
|
||||
* Note that since this operation is very I/O **expensive** (`man git-filter-branch`)
|
||||
* provides a **rich** interface for scripts to print disk layouts (`man fdisk`)
|
||||
* The number of times the softirq handler function terminated per second because its **budget** was consumed (`man sar.sysstat`)
|
||||
* the extra **cost** depends a lot on the application at hand (`man valgrind`)
|
||||
|
||||
|
||||
|
||||
### more miscellaneous metaphors
|
||||
|
||||
here are some more I found that didn’t fit into any of those categories yet.
|
||||
|
||||
* when a thread is created under glibc, just one **big** lock is used for all thread setup (`man valgrind`)
|
||||
* will likely **drop** the connection (`man x11vnc`)
|
||||
* on all **paths** from the load to the function entry (`man gcc`)
|
||||
* it is a very good idea to **wipe** filesystem signatures, data, etc. before (`man cryptsetup`)
|
||||
* they will be **embedded** into the document
|
||||
* the client should automatically **follow** referrals returned
|
||||
* even if there exist mappings that **cover** the whole address space requested (`man mremap`)
|
||||
* when a network interface **disappears** (`man systemd-resolve`)
|
||||
|
||||
|
||||
|
||||
### we’re all using metaphors all the time
|
||||
|
||||
I found a lot more metaphors than I expected, and most of them are just part of how I’d normally talk about a program. Interesting!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://jvns.ca/blog/2020/05/08/metaphors-in-man-pages/
|
||||
|
||||
作者:[Julia Evans][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://jvns.ca/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.youtube.com/watch?v=K8MF3aDg-bM&feature=youtu.be&t=14991
|
||||
[2]: https://www.goodreads.com/book/show/34459.Metaphors_We_Live_By
|
||||
[3]: https://metaphor.icsi.berkeley.edu/pub/en/index.php/Category:Metaphor
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user