mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-01-13 22:30:37 +08:00
Merge branch 'master' of https://github.com/LCTT/TranslateProject into translating
This commit is contained in:
commit
60e16e302d
@ -1,20 +1,22 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (lkxed)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (turbokernel)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-14747-1.html)
|
||||
[#]: subject: (Docker Compose: a nice way to set up a dev environment)
|
||||
[#]: via: (https://jvns.ca/blog/2021/01/04/docker-compose-is-nice/)
|
||||
[#]: author: (Julia Evans https://jvns.ca/)
|
||||
|
||||
Docker Compose:搭建开发环境的好办法
|
||||
Docker Compose:搭建开发环境的好方式
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202206/23/180033lpg4v4bz0bbb1719.jpg)
|
||||
|
||||
大家好!我又写了一篇关于 [我最喜欢的电脑工具][1] 的文章。这一篇讲的是 Docker Compose!
|
||||
|
||||
这篇文章主要就是讲一讲我对 Docker Compose 有多么满意啦(不讨论它的缺点)!咳咳,因为它总能够完成它该做的,并且似乎总能奏效,更棒的是,它使用起来还非常简单。另外,在本文中,我只讨论我是怎么用 Docker Compose 来搭建开发环境的,而不涉及它在生产中的使用。
|
||||
本文主要就是讲一讲我对 Docker Compose 有多么满意啦(不讨论它的缺点)!咳咳,因为它总能够完成它该做的,并且似乎总能有效,更棒的是,它的使用还非常简单。另外,在本文中,我只讨论我是如何用 Docker Compose 来搭建开发环境的,而不涉及它在生产中的使用。
|
||||
|
||||
最近,我考虑了很多关于这种搭建个人开发环境的方式,原因是,我现在把所有的计算工作都搬到了一个私有云上,大概 20 美元/月的样子。这样一来,我就不用在工作的时候花时间去思考应该如何管理几千台 AWS 服务器了。
|
||||
最近,我考虑了很多关于这种个人开发环境的搭建方式,原因是,我现在把所有的计算工作都搬到了一个私有云上,大概 20 美元/月的样子。这样一来,我就不用在工作的时候花时间去思考应该如何管理几千台 AWS 服务器了。
|
||||
|
||||
在此之前,我曾花了两天的时间,尝试使用其他的工具来尝试搭建一个开发环境,搭到后面,我实在是心累了。相比起来,Docker Compose 就简单易用多了,我非常满意。于是,我和妹妹分享了我的 `docker-compose` 使用经历,她略显惊讶:“是吧!你也觉得 Docker Compose 真棒对吧!” 嗯,我觉得我应该写一篇博文把过程记录下来,于是就有了你们看到的这篇文章。
|
||||
|
||||
@ -24,10 +26,10 @@ Docker Compose:搭建开发环境的好办法
|
||||
|
||||
* 一个 Nginx 服务器
|
||||
* 一个 Rails 服务
|
||||
* 一个 Go 服务 (使用了 [gotty][2] 来代理一些 SSH 连接)
|
||||
* 一个 Go 服务(使用了 [gotty][2] 来代理一些 SSH 连接)
|
||||
* 一个 Postgres 数据库
|
||||
|
||||
在本地搭建 Rails 服务非常简单,用不着容器(我只需要安装 Postgres 和 Ruby 就行了,小菜一碟)。但是,我还想要把匹配 `/proxy/*` 的请求的发送到 Go 服务,其他所有请求都发送到 Rails 服务,所以我还要用到 Nginx。问题来了,在笔记本电脑上安装 Nginx 对我来说太麻烦了。
|
||||
在本地搭建 Rails 服务非常简单,用不着容器(我只需要安装 Postgres 和 Ruby 就行了,小菜一碟)。但是,我还想要把匹配 `/proxy/*` 的请求的发送到 Go 服务,其他所有请求都发送到 Rails 服务,所以需要借助 Nginx。问题来了,在笔记本电脑上安装 Nginx 对我来说太麻烦了。
|
||||
|
||||
是时候使用 `docker-compose` 了!
|
||||
|
||||
@ -35,7 +37,7 @@ Docker Compose:搭建开发环境的好办法
|
||||
|
||||
基本上,Docker Compose 的作用就是允许你运行一组可以互相通信 Docker 容器。
|
||||
|
||||
你可以在一个叫做 `docker-compose.yml` 的文件中,配置你所有的容器。下面,我将贴上我为这个服务编写的 `docker-compose.yml` 文件(的全部内容),因为我觉得它真的很简洁、直接!
|
||||
你可以在一个叫做 `docker-compose.yml` 的文件中,配置你所有的容器。我在下方将贴上我为这个服务编写的 `docker-compose.yml` 文件(完整内容),因为我觉得它真的很简洁、直接!
|
||||
|
||||
```
|
||||
version: "3.3"
|
||||
@ -63,7 +65,7 @@ services:
|
||||
- "8777:80" # this exposes port 8777 on my laptop
|
||||
```
|
||||
|
||||
这个配置包含了两种容器。对于前面两个容器,我不加修改地使用了既有的镜像(`image: postgres` 和 `image: ubuntu`)。对于后面两个,我不得不构建一个自定义容器镜像,其中, `build: docker/rails` 的作用就是告诉 Docker Compose,它应该使用 `docker/rails/Dockerfile` 来构建一个自定义容器。
|
||||
这个配置包含了两种容器。对于前面两个容器,我直接使用了现有的镜像(`image: postgres` 和 `image: ubuntu`)。对于后面两个容器,我不得不构建一个自定义容器镜像,其中, `build: docker/rails` 的作用就是告诉 Docker Compose,它应该使用 `docker/rails/Dockerfile` 来构建一个自定义容器。
|
||||
|
||||
我需要允许我的 Rails 服务访问一些 API 密钥和其他东西,因此,我使用了 `source secrets.sh`,它的作用就是在环境变量中预设一组密钥。
|
||||
|
||||
@ -71,11 +73,11 @@ services:
|
||||
|
||||
我一直都是先运行 `docker-compose build` 来构建容器,然后再运行 `docker-compose up` 把所有服务启动起来。
|
||||
|
||||
你可以在 yaml 文件中设置 `depends_on`,这样你可以获得更多容器启动时的控制。不过,对于我的这些服务而言,启动顺序并不重要,所以我没有设置它。
|
||||
你可以在 yaml 文件中设置 `depends_on`,从而进行更多启动容器的控制。不过,对于我的这些服务而言,启动顺序并不重要,所以我没有设置它。
|
||||
|
||||
### 使用网络通信也非常简单
|
||||
### 网络互通也非常简单
|
||||
|
||||
有件很重要的事:容器之间得能够互相连接才行。Docker Compose 让这件事变得超级简单!假设我有一个 Rails 服务正在名为 `rails_server` 的容器中运行,端口是 3000,那么我就可以通过 `http://rails_server:3000` 来访问该服务。就是这么简单!
|
||||
容器之间的互通也是一件很重要的事情。Docker Compose 让这件事变得超级简单!假设我有一个 Rails 服务正在名为 `rails_server` 的容器中运行,端口是 3000,那么我就可以通过 `http://rails_server:3000` 来访问该服务。就是这么简单!
|
||||
|
||||
以下代码片段截取自我的 Nginx 配置文件,它是根据我的使用需求配置的(我删除了许多 `proxy_set_headers` 行,让它看起来更清楚):
|
||||
|
||||
@ -88,7 +90,7 @@ location @app {
|
||||
}
|
||||
```
|
||||
|
||||
或者,你也看下面这个代码片段,它截取自我的 Rails 项目的数据库配置,我在其中使用了数据库容器的名称(`db`):
|
||||
或者,你可以参考如下代码片段,它截取自我的 Rails 项目的数据库配置,我在其中使用了数据库容器的名称(`db`):
|
||||
|
||||
```
|
||||
development:
|
||||
@ -118,13 +120,13 @@ $ dig +short @127.0.0.11 go_server
|
||||
|
||||
以下所有命令都是在容器外执行的,因为我没有在容器里安装很多网络工具。
|
||||
|
||||
**第一步:**:使用 `ps aux | grep puma`,找到我的 Rails 服务的进程 ID。
|
||||
**第一步:**:使用 `ps aux | grep puma`,获取 Rails 服务的进程 ID。
|
||||
|
||||
找到了,它是 `1837916`!感觉不错哦~
|
||||
找到了,它是 `1837916`!简单~
|
||||
|
||||
**第二步:**:找到和 `1837916` 运行在同一个网络命名空间的 UDP 服务。
|
||||
|
||||
我使用了 `nsenter` 来在 `puma` 进程的网络命令空间内运行 `netstat`(理论上,我猜想你也可以使用 `netstat -tupn` 来只显示 UDP 服务,但此时,我的手指头只会打出 `netstat -tulpn`)。
|
||||
我使用了 `nsenter` 来在 `puma` 进程的网络命令空间内运行 `netstat`(理论上,我猜想你也可以使用 `netstat -tupn` 来只显示 UDP 服务,但此时,我的手指头只习惯于打出 `netstat -tulpn`)。
|
||||
|
||||
```
|
||||
$ sudo nsenter -n -t 1837916 netstat -tulpn
|
||||
@ -152,7 +154,7 @@ $ sudo nsenter -n -t 1837916 dig +short @127.0.0.11 59426 rails_server
|
||||
|
||||
对于类似“这个服务似乎正运行在 X 端口上,但我却在 Y 端口上访问到了它,这是什么回事呢?”的问题,我的第一念头都是“一定是 iptables 在作怪”。
|
||||
|
||||
于是,我运行了容器的网络命令空间内运行了 `iptables-save`,果不其然,真相大白:
|
||||
于是,我在运行了容器的网络命令空间内执行 `iptables-save`,果不其然,真相大白:
|
||||
|
||||
```
|
||||
$ sudo nsenter -n -t 1837916 iptables-save
|
||||
@ -165,13 +167,13 @@ COMMIT
|
||||
|
||||
### 数据库文件储存在一个临时目录中
|
||||
|
||||
这样做有一个好处:我可以直接挂载 Postgres 容器的数据目录 `./tmp/db`,而不需要在我的笔记本电脑上管理 Postgres 环境。
|
||||
这样做有一个好处:我可以直接挂载 Postgres 容器的数据目录 `./tmp/db`,而无需在我的笔记本电脑上管理 Postgres 环境。
|
||||
|
||||
我很喜欢这种方式,因为我真的不想在笔记本电脑上,亲自管理一个 Postgres 环境(我也真的不知道该如何配置 Postgres)。还有就是,出于习惯,我更喜欢让开发环境的数据库和代码放在同一个目录下。
|
||||
我很喜欢这种方式,因为我真的不想在笔记本电脑上独自管理一个 Postgres 环境(我也真的不知道该如何配置 Postgres)。另外,出于习惯,我更喜欢让开发环境的数据库和代码放在同一个目录下。
|
||||
|
||||
### 仅需一行命令,我就可以访问 Rails 控制台
|
||||
|
||||
管理 Ruby 的版本总是有点棘手,并且,即使我暂时搞定了它,我也总是有点担心自己会把 Ruby 环境搞坏,然后就要修它修个十年(夸张)。
|
||||
管理 Ruby 的版本总是有点棘手,并且,即使我暂时搞定了它,我也总是有点担心自己会把 Ruby 环境搞坏,然后就要修它个十年(夸张)。
|
||||
|
||||
(使用 Docker Compose)搭建好这个开发环境后,如果我需要访问 Rails <ruby>控制台<rt>console</rt></ruby>(一个交互式环境,加载了所有我的 Rails 代码),我只需要运行一行代码即可:
|
||||
|
||||
@ -188,31 +190,31 @@ irb(main):001:0>
|
||||
|
||||
我碰到了一个问题:Rails 控制台的历史记录丢失了,因为我一直在不断地重启它。
|
||||
|
||||
不过,我也找到了一个相当简单的解决方案(嘿嘿):我往容器中添加了一个 `/root/.irbrc` 文件,它能够把 IRB 历史记录文件的保存位置,修改到一个不受容器重启影响的地方。只需要一行代码就够啦:
|
||||
不过,我也找到了一个相当简单的解决方案(嘿嘿):我往容器中添加了一个 `/root/.irbrc` 文件,它能够把 IRB 历史记录文件的保存位置指向一个不受容器重启影响的地方。只需要一行代码就够啦:
|
||||
|
||||
```
|
||||
IRB.conf[:HISTORY_FILE] = "/app/tmp/irb_history"
|
||||
```
|
||||
|
||||
### 我还是不知道它在生产环境的表现会怎么样
|
||||
### 我还是不知道它在生产环境的表现如何
|
||||
|
||||
到目前为止,这个项目的生产环境搭建进度,还停留在“我制作了一个 digitalocean droplet(LCCT 译注:一种 Linux 虚拟机服务),并手工编辑了很多文件”的阶段。
|
||||
到目前为止,这个项目的生产环境搭建进度,还停留在“我制作了一个 DigitalOcean droplet(LCCT 译注:一种 Linux 虚拟机服务),并手工编辑了很多文件”的阶段。
|
||||
|
||||
嗯……我相信我以后会在生产环境中使用 docker-compose 来运行一下它的。我猜它能够会正常工作,因为这个服务很可能最多只有两个用户在使用,并且,如果我愿意,我可以容忍它在部署过程中有 60 秒的不可用时间。不过话又说回来,出错的往往是我想不到的地方。
|
||||
嗯……我相信以后会在生产环境中使用 docker-compose 来运行一下它的。我猜它能够正常工作,因为这个服务很可能最多只有两个用户在使用,并且,如果我愿意,我可以容忍它在部署过程中有 60 秒的不可用时间。不过话又说回来,出错的往往是我想不到的地方。
|
||||
|
||||
推特网友提供了一些在生产中使用 docker-compose 的注意事项:
|
||||
|
||||
* `docker-compose up` 只会重启那些需要重启的容器,这会让重启速度更快。
|
||||
* 有一个 Bash 小脚本 [wait-for-it][3],你可以用它来让一个容器保持等待,直到另一个容器的服务可用。
|
||||
* 你可以准备两份 `docker-compose.yaml` 文件:用于开发环境的 `docker-compose.yaml` 和 用于生产环境的 `docker-compose-prod.yaml`。我想我会在分别为 Nginx 指定不同的端口:开发时使用 `8999`,生产中使用 `80`。
|
||||
* 有一个 Bash 小脚本 [wait-for-it][3],你可以用它来保持等待一个容器,直到另一个容器的服务可用。
|
||||
* 你可以准备两份 `docker-compose.yaml` 文件:用于开发环境的 `docker-compose.yaml` 和用于生产环境的 `docker-compose-prod.yaml`。我想我会在分别为 Nginx 指定不同的端口:开发时使用 `8999`,生产中使用 `80`。
|
||||
* 人们似乎一致认为,如果你的项目是一台计算机上运行的小网站,那么 docker-compose 在生产中不会有问题。
|
||||
* 有个人建议说,如果愿意在生产环境搭建复杂那么一丢丢,Docker Swarm 就或许会是更好的选择,不过我还没试过(当然,如果要这么说的话,干嘛不用 Kubernetes 呢?Docker Compose 的意义就是它超级简单,而 Kubernetes 肯定不简单 :))。
|
||||
* 有个人建议说,如果愿意在生产环境搭建复杂那么一丢丢,Docker Swarm 就或许会是更好的选择,不过我还没试过(当然,如果要这么说的话,干嘛不用 Kubernetes 呢?Docker Compose 的意义就是它超级简单,而 Kubernetes 肯定不简单 : ))。
|
||||
|
||||
Docker 似乎还有一个特性,它能够 [把你用 docker-compose 搭建的环境,自动推送到弹性容器服务(ESC)上][4],听上去好酷的样子,但是我还没有试过。
|
||||
|
||||
### docker-compose 会有不适用的场景吗
|
||||
|
||||
我听说 docker-compose 在以下场景的表现差强人意:
|
||||
我听说 docker-compose 在以下场景的表现较差:
|
||||
|
||||
* 当你有很多微服务的时候(还是自己搭建比较好)
|
||||
* 当你尝试从一个很大的数据库中导入数据时(就像把几百 G 的数据存到每个人的笔记本电脑里一样)
|
||||
@ -231,7 +233,7 @@ via: https://jvns.ca/blog/2021/01/04/docker-compose-is-nice/
|
||||
作者:[Julia Evans][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[lkxed](https://github.com/lkxed)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[turbokernel](https://github.com/turbokernel)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -3,35 +3,38 @@
|
||||
[#]: author: "Ankush Das https://news.itsfoss.com/author/ankush/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: "lkxed"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-14746-1.html"
|
||||
|
||||
Ubuntu 在 Google Nest Hub 上运行,等等,什么?
|
||||
Ubuntu 可以运行在谷歌 Nest Hub 上了?!
|
||||
======
|
||||
一名黑客成功地在 Google Nest Hub(第二代)上运行了 Ubuntu,嗯,然后呢?
|
||||
|
||||
> 一名安全专家成功地在谷歌 Nest Hub(第 2 代)上运行了 Ubuntu,嗯,然后呢?
|
||||
|
||||
![Ubuntu Google][1]
|
||||
|
||||
我刚刚看到了一个关于在 Google Nest Hub(第二代)上运行的 Ubuntu 的故事。
|
||||
我刚刚看到了一个关于在谷歌 Nest Hub(第 2 代)上运行的 Ubuntu 的消息。
|
||||
|
||||
嗯,这实在是让人兴奋!
|
||||
|
||||
所以,让我在这里分享更多关于它的信息吧。
|
||||
|
||||
### 破解 Google Nest Hub 以安装 Ubuntu
|
||||
### 破解谷歌 Nest Hub 以安装 Ubuntu
|
||||
|
||||
是的,一次黑客攻击使这成为可能。
|
||||
是的,破解使得这成为可能。
|
||||
|
||||
网络安全专家 **Frédéric Basse** 破解了 Google Nest Hub(第 2 代)的安全启动,并成功运行 Ubuntu。
|
||||
网络安全专家 Frédéric Basse 破解了谷歌 Nest Hub(第 2 代)的安全启动,并成功运行 Ubuntu。
|
||||
|
||||
当然,Google Nest Hub 并没有正式支持启动一个自定义操作系统。但是,Fred 使用了一个安全漏洞,从而成功运行了 Ubuntu。
|
||||
当然,谷歌 Nest Hub 并没有正式支持启动一个自定义操作系统。但是,Fred 使用了一个安全漏洞,从而成功运行了 Ubuntu。
|
||||
|
||||
虽然这很有趣,但对于始终连接的谷歌智能家居显示器来说,这也是一个严重的问题。
|
||||
虽然这很有趣,但对于始终在线的谷歌智能家居显示器来说,这也是一个严重的安全问题。
|
||||
|
||||
正如他在 [博客文章][2] 中所解释的,黑客使用了 Raspberry Pi Pico 微控制器,利用引导加载程序中的 USB 漏洞,从而破坏了安全启动链。
|
||||
![](https://news.itsfoss.com/wp-content/uploads/2022/06/ubuntu-google-nest-hacked.gif)
|
||||
|
||||
安全专家得出结论:
|
||||
正如这位安全专家在 [博客文章][2] 中所解释的,他使用了树莓派 Pico 微控制器,利用引导加载程序中的 USB 漏洞,从而破坏了安全启动链。
|
||||
|
||||
这位安全专家得出结论:
|
||||
|
||||
> 因此,攻击者可以通过插入恶意 USB 设备并按下两个按钮,从而在早期启动阶段(内核执行之前)执行任意代码。
|
||||
|
||||
@ -41,21 +44,21 @@ Ubuntu 在 Google Nest Hub 上运行,等等,什么?
|
||||
|
||||
![][4]
|
||||
|
||||
该漏洞允许攻击者启动未签名的操作系统。但是,在那之前,攻击者必须对为 Raspberry Pi(64 位 ARM)量身定制的预装 Ubuntu 镜像进行一些修改。
|
||||
该漏洞允许攻击者启动未签名的操作系统。但是,在那之前,攻击者必须对为树莓派(64 位 ARM 版)量身定制的预装 Ubuntu 镜像进行一些修改。
|
||||
|
||||
攻击者还提到了以下内容:
|
||||
这位安全专家还提到了以下内容:
|
||||
|
||||
> 我们构建了一个自定义 U-Boot 引导加载程序,禁用了安全引导,并更改了引导流程以从 USB 闪存驱动器加载环境。我们还为 elaine 构建了一个自定义 Linux 内核,其中包括包括了一些 [额外驱动,例如 USB 鼠标][5] 。来自 Ubuntu 的初始 ramdisk(initrd)被重新打包,以集成触摸屏所需的固件二进制文件。引导镜像是基于自定义 Linux 内核和修改的 initrd 创建的。
|
||||
> 我们构建了一个自定义 U-Boot 引导加载程序,禁用了安全引导,并更改了引导流程以从 USB 闪存驱动器加载环境。我们还为 elaine 构建了一个自定义 Linux 内核,其中包括包括了一些 [额外驱动,例如 USB 鼠标][5] 。重新打包了来自 Ubuntu 的初始 ramdisk(initrd),以集成触摸屏所需的固件二进制文件。引导镜像是基于自定义 Linux 内核和修改的 initrd 创建的。
|
||||
|
||||
因此,很明显,你不会获得完整的 Ubuntu 体验,但由于该漏洞,我们现在知道,如果你愿意破解 Google Nest 进行测试的话,Ubuntu 是可以在 Google Nest 上作运行的,作为一个实验(不要那样做,真的!)。
|
||||
因此,很明显,你不会获得完整的 Ubuntu 体验,但由于该漏洞,我们现在知道,如果你愿意破解 谷歌 Nest 进行测试的话(真心不建议!),Ubuntu 是可以在谷歌 Nest 上作运行的。
|
||||
|
||||
### 智能家居安全担忧 + Linux
|
||||
|
||||
网络安全专家指出,该漏洞已在上游修复(两次)。
|
||||
网络安全专家指出,该漏洞已在上游(两次)修复。
|
||||
|
||||
但是,研究人员也指出,缺乏 CVE 可能会导致修复程序无法向下游传播。
|
||||
但是,研究人员也指出,缺乏分配的 CVE 编号可能会导致修复程序无法向下游传播。
|
||||
|
||||
毫无疑问,看到有人在不受支持的设备上运行 Linux 真是太棒了。这让我思考,我们是否应该也制造一些**由 Linux 驱动的商业智能家居设备?**
|
||||
毫无疑问,看到有人在不受支持的设备上运行 Linux 真是太棒了。这让我思考,我们是否应该也制造一些 **由 Linux 驱动的商业智能家居设备?**
|
||||
|
||||
*或者说,已经有类似的东西了吗?*
|
||||
|
||||
@ -72,7 +75,7 @@ via: https://news.itsfoss.com/ubuntu-google-nest/
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[lkxed](https://github.com/lkxed)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -3,14 +3,16 @@
|
||||
[#]: author: "Abhishek Prakash https://itsfoss.com/author/abhishek/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: "geekpi"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-14748-1.html"
|
||||
|
||||
用 Curtail GUI 应用轻松压缩 Linux 中的图像
|
||||
======
|
||||
|
||||
有一大堆文件大小的图片占用了太多的磁盘空间?或者你必须将图片上传到有文件大小限制的门户网站?
|
||||
![](https://img.linux.net.cn/data/attachment/album/202206/23/182901s4d060uu98g8qquv.jpg)
|
||||
|
||||
有一大堆文件尺寸巨大的图片占用了太多的磁盘空间?或者你必须将图片上传到有文件大小限制的门户网站?
|
||||
|
||||
你可能有很多原因想要压缩图片。有大量的工具可以帮助你,我在这里说的不是命令行的工具。
|
||||
|
||||
@ -56,7 +58,7 @@ flatpak install flathub com.github.huluti.Curtail
|
||||
|
||||
正如你在上面的图片中看到的,我的一张图片的尺寸减少了 35%,另外两张图片的尺寸减少了 3% 和 8%。这是在无损压缩的情况下。
|
||||
|
||||
这些图片以 -min 为后缀(默认),保存在与原始图片相同的目录中。
|
||||
这些图片以 `-min` 为后缀(默认),保存在与原始图片相同的目录中。
|
||||
|
||||
虽然它看起来很简约,但有几个选项可以配置 Curtail。点击菜单,你会看到一些设置选项。
|
||||
|
||||
@ -87,7 +89,7 @@ via: https://itsfoss.com/curtail-image-compress/
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,18 +1,20 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Solve a charity's problem with the Julia programming language)
|
||||
[#]: via: (https://opensource.com/article/21/1/solve-problem-julia)
|
||||
[#]: author: (Chris Hermansen https://opensource.com/users/clhermansen)
|
||||
[#]: subject: "Solve a charity's problem with the Julia programming language"
|
||||
[#]: via: "https://opensource.com/article/21/1/solve-problem-julia"
|
||||
[#]: author: "Chris Hermansen https://opensource.com/users/clhermansen"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Solve a charity's problem with the Julia programming language
|
||||
======
|
||||
See how Julia differs from Java, Python, and Groovy to solve a food
|
||||
bank's real-world problem.
|
||||
See how Julia differs from Java, Python, and Groovy to solve a food bank's real-world problem.
|
||||
|
||||
![Puzzle pieces coming together to form a computer screen][1]
|
||||
|
||||
Image by: Opensource.com
|
||||
|
||||
I have been writing a series of articles about solving a nice, small, and somewhat unusual problem in different programming languages ([Groovy][2], [Python][3], and [Java][4] so far).
|
||||
|
||||
Briefly, the problem is how to unpack bulk supplies into their units (for example, dividing a 10 pack of one-pound bags of your favorite coffee) and repackage them into hampers of similar value to distribute to struggling neighbors in the community.
|
||||
@ -29,10 +31,9 @@ But enough speculation, let's code something!
|
||||
|
||||
### The Julia solution
|
||||
|
||||
My first decision is how to implement the data model. Julia supports _composite types_, seemingly similar to `struct` in C, and Julia even uses the keyword `struct`. Of note is that a `struct` is immutable (unless declared a `mutable struct`), which is fine for this problem since the data doesn't need to be mutated.
|
||||
|
||||
By following the approach I took in the Java solution, the `Unit struct` can be defined as:
|
||||
My first decision is how to implement the data model. Julia supports *composite types*, seemingly similar to `struct` in C, and Julia even uses the keyword `struct`. Of note is that a `struct` is immutable (unless declared a `mutable struct` ), which is fine for this problem since the data doesn't need to be mutated.
|
||||
|
||||
By following the approach I took in the Java solution, the `Unit struct` can be defined as:
|
||||
|
||||
```
|
||||
struct Unit
|
||||
@ -44,13 +45,12 @@ end
|
||||
|
||||
Similarly, `Pack` is defined as the bulk package of `Unit` instances:
|
||||
|
||||
|
||||
```
|
||||
struct Pack
|
||||
unit::Unit
|
||||
count::Int
|
||||
Pack(item, brand, unitCount,p ackPrice) =
|
||||
new(Unit(item, brand, [div][6](packPrice,unitCount)), unitCount)
|
||||
new(Unit(item, brand, div(packPrice,unitCount)), unitCount)
|
||||
end
|
||||
```
|
||||
|
||||
@ -58,7 +58,6 @@ There is an interesting thing here: a Julia "inner constructor." In the Java sol
|
||||
|
||||
Because Julia isn't object-oriented, I can't add methods to `Pack` to give unit price vs. pack price or to unpack it into a list of `Unit` instances. I can declare "getter" functions that accomplish the same tasks. (I probably don't need these, but I'll do it anyway to see how Julia methods work):
|
||||
|
||||
|
||||
```
|
||||
item(pack::Pack) = pack.unit.item
|
||||
brand(pack::Pack) = pack.unit.brand
|
||||
@ -68,17 +67,16 @@ packPrice(pack::Pack) = pack.unit.price * pack.count
|
||||
unpack(pack::Pack) = Iterators.collect(Iterators.repeated(pack.unit,pack.count))
|
||||
```
|
||||
|
||||
The `unpack()` method is quite similar to the method of the same name I declared in the Java class `Pack`. The function `Iterators.repeated(thing,N)` creates an iterator that will deliver `N` copies of `thing`. The `Iterators.collect` (`iterator`) function processes the `iterator` to yield an array made up of the elements it delivers.
|
||||
|
||||
Finally, the `Bought struct`:
|
||||
The `unpack()` method is quite similar to the method of the same name I declared in the Java class `Pack`. The function `Iterators.repeated(thing,N)` creates an iterator that will deliver `N` copies of `thing`. The `Iterators.collect` (`iterator` ) function processes the `iterator` to yield an array made up of the elements it delivers.
|
||||
|
||||
Finally, the `Bought struct` :
|
||||
|
||||
```
|
||||
struct Bought
|
||||
pack::Pack
|
||||
count::Int
|
||||
end
|
||||
unpack(bought::Bought) =
|
||||
unpack(bought::Bought) =
|
||||
Iterators.collect(Iterators.flatten(Iterators.repeated(unpack(bought.pack),
|
||||
bought.count)))
|
||||
```
|
||||
@ -87,7 +85,6 @@ Once again, I'm creating an array of an array of unpacked `Pack` instances (i.e.
|
||||
|
||||
Now I can construct the list of what I bought:
|
||||
|
||||
|
||||
```
|
||||
packs = [
|
||||
Bought(Pack("Rice","Best Family",10,5650),1),
|
||||
@ -111,12 +108,11 @@ I'm starting to see a pattern here… this looks surprisingly like the Java solu
|
||||
|
||||
With the list packs of what I bought, I can now unpack into the units before working on redistributing them:
|
||||
|
||||
|
||||
```
|
||||
`units = Iterators.collect(Iterators.flatten(unpack.(packs)))`
|
||||
units = Iterators.collect(Iterators.flatten(unpack.(packs)))
|
||||
```
|
||||
|
||||
What's going on here? Well, a construct like `unpack.(packs)`—that is, the dot between the function name and the argument list—applies the function `unpack()` to each element in the list `packs`. This will generate a list of lists corresponding to the unpacked groups of `Packs` I bought. To turn that into a flat list of units, I apply `Iterators.flatten()`. Because `Iterators.flatten()` is lazy, to make the flatten thing happen, I wrap it in `Iterators.collect()`. This kind of composition of functions adheres to the spirit of functional programming, even though you don't see the functions chained together, as programmers who write functionally in JavaScript, Java, or what-have-you are familiar with.
|
||||
What's going on here? Well, a construct like `unpack.(packs)` —that is, the dot between the function name and the argument list—applies the function `unpack()` to each element in the list `packs`. This will generate a list of lists corresponding to the unpacked groups of `Packs` I bought. To turn that into a flat list of units, I apply `Iterators.flatten()`. Because `Iterators.flatten()` is lazy, to make the flatten thing happen, I wrap it in `Iterators.collect()`. This kind of composition of functions adheres to the spirit of functional programming, even though you don't see the functions chained together, as programmers who write functionally in JavaScript, Java, or what-have-you are familiar with.
|
||||
|
||||
One observation is that the list of units created here is actually an array whose starting index is 1, not 0.
|
||||
|
||||
@ -124,63 +120,59 @@ With units being the list of units purchased and unpacked, I can now take on rep
|
||||
|
||||
Here's the code, which is not exceptionally different than the versions in Groovy, Python, and Java:
|
||||
|
||||
|
||||
```
|
||||
1 valueIdeal = 5000
|
||||
2 valueMax = round(valueIdeal * 1.1)
|
||||
3 hamperNumber = 0
|
||||
|
||||
4 while length(units) > 0
|
||||
4 while length(units) > 0
|
||||
5 global hamperNumber += 1
|
||||
6 hamper = Unit[]
|
||||
7 value = 0
|
||||
8 canAdd = true
|
||||
9 while canAdd
|
||||
10 u = [rand][7](0:(length(units)-1))
|
||||
10 u = rand(0:(length(units)-1))
|
||||
11 canAdd = false
|
||||
12 for o = 0:(length(units)-1)
|
||||
13 uo = (u + o) % length(units) + 1
|
||||
14 unit = units[uo]
|
||||
15 if length(units) < 3 || findfirst(u -> u == unit,hamper) === nothing && (value + unit.price) < valueMax
|
||||
15 if length(units) < 3 || findfirst(u -> u == unit,hamper) === nothing && (value + unit.price) < valueMax
|
||||
16 push!(hamper,unit)
|
||||
17 value += unit.price
|
||||
18 deleteat!(units,uo)
|
||||
19 canAdd = length(units) > 0
|
||||
19 canAdd = length(units) > 0
|
||||
20 break
|
||||
21 end
|
||||
22 end
|
||||
23 end
|
||||
24 Printf.@[printf][8]("\nHamper %d value %d:\n",hamperNumber,value)
|
||||
24 Printf.@printf("\nHamper %d value %d:\n",hamperNumber,value)
|
||||
25 for unit in hamper
|
||||
26 Printf.@[printf][8]("%-25s%-25s%7d\n",unit.item,unit.brand,unit.price)
|
||||
26 Printf.@printf("%-25s%-25s%7d\n",unit.item,unit.brand,unit.price)
|
||||
27 end
|
||||
28 Printf.@[printf][8]("Remaining units %d\n",length(units))
|
||||
28 Printf.@printf("Remaining units %d\n",length(units))
|
||||
29 end
|
||||
```
|
||||
|
||||
Some clarification, by line numbers:
|
||||
|
||||
* Lines 1–3: Set up the ideal and maximum values to be loaded into any given hamper and initialize Groovy's random number generator and the hamper number
|
||||
* Lines 4–29: This `while` loop redistributes units into hampers, as long as there are more available
|
||||
* Lines 5–7: Increment the (global) hamper number, get a new empty hamper (an array of `Unit` instances), and set its value to 0
|
||||
* Line 8 and 9–23: As long as I can add units to the hamper…
|
||||
* Line 10: Gets a random number between zero and the number of remaining units minus 1
|
||||
* Line 11: Assumes I can't find more units to add
|
||||
* Lines 12–22: This `for` loop, starting at the randomly chosen index, will try to find a unit that can be added to the hamper
|
||||
* Lines 13–14: Figure out which unit to look at (remember arrays start at index 1) and get it
|
||||
* Lines 15–21: I can add this unit to the hamper if there are only a few left or if the value of the hamper isn't too high once the unit is added and if that unit isn't already in the hamper
|
||||
* Lines 16–18: Add the unit to the hamper, increment the hamper value by the unit price, and remove the unit from the available units list
|
||||
* Lines 19–20: As long as there are units left, I can add more, so break out of this loop to keep looking
|
||||
* Line 22: On exit from this `for` loop, if I have inspected every remaining unit and could not find one to add to the hamper, the hamper is complete; otherwise, I found one and can continue looking for more
|
||||
* Line 23: On exit from this `while` loop, the hamper is as full as I can make it, so…
|
||||
* Lines 24–28: Print out the contents of the hamper and the remaining units info
|
||||
* Line 29: When I exit this loop, there are no more units left
|
||||
|
||||
|
||||
* Lines 1–3: Set up the ideal and maximum values to be loaded into any given hamper and initialize Groovy's random number generator and the hamper number
|
||||
* Lines 4–29: This `while` loop redistributes units into hampers, as long as there are more available
|
||||
* Lines 5–7: Increment the (global) hamper number, get a new empty hamper (an array of `Unit` instances), and set its value to 0
|
||||
* Line 8 and 9–23: As long as I can add units to the hamper…
|
||||
* Line 10: Gets a random number between zero and the number of remaining units minus 1
|
||||
* Line 11: Assumes I can't find more units to add
|
||||
* Lines 12–22: This `for` loop, starting at the randomly chosen index, will try to find a unit that can be added to the hamper
|
||||
* Lines 13–14: Figure out which unit to look at (remember arrays start at index 1) and get it
|
||||
* Lines 15–21: I can add this unit to the hamper if there are only a few left or if the value of the hamper isn't too high once the unit is added and if that unit isn't already in the hamper
|
||||
* Lines 16–18: Add the unit to the hamper, increment the hamper value by the unit price, and remove the unit from the available units list
|
||||
* Lines 19–20: As long as there are units left, I can add more, so break out of this loop to keep looking
|
||||
* Line 22: On exit from this `for` loop, if I have inspected every remaining unit and could not find one to add to the hamper, the hamper is complete; otherwise, I found one and can continue looking for more
|
||||
* Line 23: On exit from this `while` loop, the hamper is as full as I can make it, so…
|
||||
* Lines 24–28: Print out the contents of the hamper and the remaining units info
|
||||
* Line 29: When I exit this loop, there are no more units left
|
||||
|
||||
The output of running this code looks quite similar to the output from the other programs:
|
||||
|
||||
|
||||
```
|
||||
Hamper 1 value 5020:
|
||||
Tea Superior 544
|
||||
@ -238,9 +230,8 @@ Once again, the random-number-driven list manipulation seems to make the "workin
|
||||
|
||||
Given that the main effort revolves around `for` and `while` loops, in Julia, I don't see any construct similar to:
|
||||
|
||||
|
||||
```
|
||||
`for (boolean canAdd = true; canAdd; ) { … }`
|
||||
for (boolean canAdd = true; canAdd; ) { … }
|
||||
```
|
||||
|
||||
This means I have to declare the `canAdd` variable outside the `while` loop. Which is too bad—but not a terrible thing.
|
||||
@ -249,27 +240,24 @@ I do miss not being able to attach behavior directly to my data, but that's just
|
||||
|
||||
Good things: low ceremony, check; decent list-handling, check; compact and readable code, check. All in all, a pleasant experience, supporting the idea that Julia can be a decent choice to solve "ordinary problems" and as a scripting language.
|
||||
|
||||
Next time, I'll do this exercise in [Go][9].
|
||||
Next time, I'll do this exercise in [Go][6].
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/1/solve-problem-julia
|
||||
|
||||
作者:[Chris Hermansen][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][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/puzzle_computer_solve_fix_tool.png?itok=U0pH1uwj (Puzzle pieces coming together to form a computer screen)
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/puzzle_computer_solve_fix_tool.png
|
||||
[2]: https://opensource.com/article/20/9/groovy
|
||||
[3]: https://opensource.com/article/20/9/solve-problem-python
|
||||
[4]: https://opensource.com/article/20/9/problem-solving-java
|
||||
[5]: https://julialang.org/
|
||||
[6]: http://www.opengroup.org/onlinepubs/009695399/functions/div.html
|
||||
[7]: http://www.opengroup.org/onlinepubs/009695399/functions/rand.html
|
||||
[8]: http://www.opengroup.org/onlinepubs/009695399/functions/printf.html
|
||||
[9]: https://golang.org/
|
||||
[6]: https://golang.org/
|
||||
|
@ -1,21 +1,23 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to customize your voice assistant with the voice of your choice)
|
||||
[#]: via: (https://opensource.com/article/21/1/customize-voice-assistant)
|
||||
[#]: author: (Rich Lucente https://opensource.com/users/rlucente)
|
||||
[#]: subject: "How to customize your voice assistant with the voice of your choice"
|
||||
[#]: via: "https://opensource.com/article/21/1/customize-voice-assistant"
|
||||
[#]: author: "Rich Lucente https://opensource.com/users/rlucente"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
How to customize your voice assistant with the voice of your choice
|
||||
======
|
||||
The Nana and Poppy project enables a voice assistant to greet users with
|
||||
their great-grandchildren's voices instead of a generic AI.
|
||||
The Nana and Poppy project enables a voice assistant to greet users with their great-grandchildren's voices instead of a generic AI.
|
||||
|
||||
![radio communication signals][1]
|
||||
|
||||
Image by: [Internet Archive Book Images][2]. Modified by Opensource.com. [CC BY-SA 4.0][3]
|
||||
|
||||
It can be hard to find meaningful gifts for relatives that already have almost everything. My wife and I have given our parents "experiences" to try something novel, such as going to a themed restaurant or seeing a concert, but as our parents get older, it becomes more difficult. This year was no exception—until I thought about a way open source could give them something really special.
|
||||
|
||||
What if when they request help from an artificial intelligence (AI) voice assistant such as [Mycroft][2], my in-laws could get a special greeting? I looked at the existing voice assistant APIs to see if something like this was already available. There was something close, but not exactly what I was looking for. My idea was to record their great-grandchildren speaking a short greeting that would play whenever they push the button and before the conversation with the voice assistant begins. The greeting would be something like:
|
||||
What if when they request help from an artificial intelligence (AI) voice assistant such as [Mycroft][4], my in-laws could get a special greeting? I looked at the existing voice assistant APIs to see if something like this was already available. There was something close, but not exactly what I was looking for. My idea was to record their great-grandchildren speaking a short greeting that would play whenever they push the button and before the conversation with the voice assistant begins. The greeting would be something like:
|
||||
|
||||
> "Good morning, Nana and Poppy. Today is December 25th. The time is 3:10 pm. The current temperature for Waynesboro is 47 degrees. The current temperature for Ocean City is 50 degrees."
|
||||
|
||||
@ -25,168 +27,166 @@ When they press the button, my in-laws would hear their great-grandchildren repo
|
||||
|
||||
The first problem was figuring out what phrases the voice assistant would need to say. Thinking about all the dates, times, and temperatures that I would need to cover, I arrived at a list of 79 phrases. I sent these instructions to my nieces:
|
||||
|
||||
> _Please record the kids saying each line below. Sorry there are so many. It's okay to do this in one setting with prompting them if it makes it easier. I can edit the audio files and deal with most formats, so none of that should be a problem. Just record using your phone in whatever way is easiest._
|
||||
>
|
||||
> _Make sure that the kids say each line clearly and loudly. There should be a slight pause between each line to make editing easier (prompting helps like "Repeat after me …"). That will make it easier for me to chop these up into individual sound files._
|
||||
>
|
||||
> _Whenever the button on the device is pushed, it will respond with a random grandchild saying the correct date/time/temperature, like:_
|
||||
>
|
||||
> _"Good afternoon, Nana and Poppy. Today is January third. The time is one oh four pm. The current temperature for Waynesboro is thirty degrees. The current temperature for Ocean City is thirty four degrees."_
|
||||
>
|
||||
> _PLEASE RECORD EACH CHILD SAYING THE FOLLOWING PHRASES WITH A SHORT PAUSE BETWEEN EACH ONE:_
|
||||
> Please record the kids saying each line below. Sorry there are so many. It's okay to do this in one setting with prompting them if it makes it easier. I can edit the audio files and deal with most formats, so none of that should be a problem. Just record using your phone in whatever way is easiest.
|
||||
|
||||
> Make sure that the kids say each line clearly and loudly. There should be a slight pause between each line to make editing easier (prompting helps like "Repeat after me …"). That will make it easier for me to chop these up into individual sound files.*
|
||||
|
||||
> Whenever the button on the device is pushed, it will respond with a random grandchild saying the correct date/time/temperature, like: "Good afternoon, Nana and Poppy. Today is January third. The time is one oh four pm. The current temperature for Waynesboro is thirty degrees. The current temperature for Ocean City is thirty four degrees."
|
||||
|
||||
> PLEASE RECORD EACH CHILD SAYING THE FOLLOWING PHRASES WITH A SHORT PAUSE BETWEEN EACH ONE:
|
||||
|
||||
Then I provided the following list of words for the children to record:
|
||||
|
||||
_Good
|
||||
morning
|
||||
afternoon
|
||||
evening
|
||||
night_
|
||||
(这个表格有问题,待修复)
|
||||
|
||||
_Nana and Poppy_
|
||||
| - | - | - |
|
||||
| :- | :- | :- |
|
||||
| Good
|
||||
morning
|
||||
afternoon
|
||||
evening
|
||||
night
|
||||
Nana and Poppy
|
||||
The time
|
||||
Today
|
||||
The current temperature for
|
||||
Waynesboro
|
||||
Ocean City
|
||||
is
|
||||
and
|
||||
degrees
|
||||
minus
|
||||
am
|
||||
pm
|
||||
January
|
||||
February
|
||||
March
|
||||
April
|
||||
May | June
|
||||
July
|
||||
August
|
||||
September
|
||||
October
|
||||
November
|
||||
December
|
||||
first
|
||||
second
|
||||
third
|
||||
fourth
|
||||
fifth
|
||||
sixth
|
||||
seventh
|
||||
eighth
|
||||
ninth
|
||||
tenth
|
||||
eleventh
|
||||
twelfth
|
||||
thirteenth
|
||||
fourteenth
|
||||
fifteenth
|
||||
sixteenth
|
||||
seventeenth
|
||||
eighteenth
|
||||
nineteenth
|
||||
twentieth
|
||||
thirtieth
|
||||
oh | one
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
six
|
||||
seven
|
||||
eight
|
||||
nine
|
||||
ten
|
||||
eleven
|
||||
twelve
|
||||
thirteen
|
||||
fourteen
|
||||
fifteen
|
||||
sixteen
|
||||
seventeen
|
||||
eighteen
|
||||
nineteen
|
||||
twenty
|
||||
thirty
|
||||
forty
|
||||
fifty
|
||||
sixty
|
||||
seventy
|
||||
eighty
|
||||
ninety
|
||||
hundred |
|
||||
|
||||
_The time
|
||||
Today
|
||||
The current temperature for
|
||||
Waynesboro
|
||||
Ocean City
|
||||
is
|
||||
and
|
||||
degrees
|
||||
minus_
|
||||
*Nana and Poppy*
|
||||
|
||||
_am
|
||||
pm_
|
||||
*The time
|
||||
Today
|
||||
The current temperature for
|
||||
Waynesboro
|
||||
Ocean City
|
||||
is
|
||||
and
|
||||
degrees
|
||||
minus*
|
||||
|
||||
_January
|
||||
February
|
||||
March
|
||||
April
|
||||
May_
|
||||
*am
|
||||
pm*
|
||||
|
||||
| _June
|
||||
July
|
||||
August
|
||||
September
|
||||
October
|
||||
November
|
||||
December
|
||||
first
|
||||
second
|
||||
third
|
||||
fourth
|
||||
fifth
|
||||
sixth
|
||||
seventh
|
||||
eighth
|
||||
ninth
|
||||
tenth
|
||||
eleventh
|
||||
twelfth
|
||||
thirteenth
|
||||
fourteenth
|
||||
fifteenth
|
||||
sixteenth
|
||||
seventeenth
|
||||
eighteenth
|
||||
nineteenth
|
||||
twentieth
|
||||
thirtieth
|
||||
oh_ | _one
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
six
|
||||
seven
|
||||
eight
|
||||
nine
|
||||
ten
|
||||
eleven
|
||||
twelve
|
||||
thirteen
|
||||
fourteen
|
||||
fifteen
|
||||
sixteen
|
||||
seventeen
|
||||
eighteen
|
||||
nineteen
|
||||
twenty
|
||||
thirty
|
||||
forty
|
||||
fifty
|
||||
sixty
|
||||
seventy
|
||||
eighty
|
||||
ninety
|
||||
hundred_
|
||||
---|---|---
|
||||
*January
|
||||
February
|
||||
March
|
||||
April
|
||||
May*
|
||||
|
||||
My nieces are doubly blessed with children under 10 years old and near-infinite patience. So, after a couple of months of prodding, I received a three-minute audio file for each child.
|
||||
|
||||
Now my problem was how to edit them. I needed to normalize the recordings, reduce noise, and chop them into audio clips for individual words and phrases. I also wanted to take advantage of lossless audio, and I decided to convert the tracks to Waveform Audio File Format ([WAV][3]). Audacity was just the open source tool to do all of that.
|
||||
Now my problem was how to edit them. I needed to normalize the recordings, reduce noise, and chop them into audio clips for individual words and phrases. I also wanted to take advantage of lossless audio, and I decided to convert the tracks to Waveform Audio File Format ([WAV][5]). Audacity was just the open source tool to do all of that.
|
||||
|
||||
### Audacity to the rescue!
|
||||
|
||||
[Audacity][4] is a feature-rich open source sound-editing tool. The software's features and capabilities can be overwhelming, so I'll describe the workflow I followed to accomplish my goals. I make no claims to being an Audacity expert, but the steps I followed seemed to work pretty well. (Comments are always welcome on how to improve what I've done.)
|
||||
[Audacity][6] is a feature-rich open source sound-editing tool. The software's features and capabilities can be overwhelming, so I'll describe the workflow I followed to accomplish my goals. I make no claims to being an Audacity expert, but the steps I followed seemed to work pretty well. (Comments are always welcome on how to improve what I've done.)
|
||||
|
||||
Audacity has [downloads][5] for Linux, Windows, and macOS. I grabbed the most recent macOS binary and quickly installed it on my laptop. Launching Audacity opens an empty new project. I imported all of the children's audio files using the **Import** feature.
|
||||
Audacity has [downloads][7] for Linux, Windows, and macOS. I grabbed the most recent macOS binary and quickly installed it on my laptop. Launching Audacity opens an empty new project. I imported all of the children's audio files using the **Import** feature.
|
||||
|
||||
![Import audio files in Audacity][6]
|
||||
|
||||
(Rich Lucente, [CC BY-SA 4.0][7])
|
||||
![Import audio files in Audacity][8]
|
||||
|
||||
#### Normalizing audio files
|
||||
|
||||
Some of the children spoke louder than others, so the various audio files had different volume levels. I needed to normalize the audio tracks so that the greeting's volume would be the same regardless of which child was speaking. To normalize the volumes, I began by selecting all of the audio tracks after they were imported.
|
||||
|
||||
![Selecting all the audio tracks][8]
|
||||
|
||||
(Rich Lucente, [CC BY-SA 4.0][7])
|
||||
![Selecting all the audio tracks][9]
|
||||
|
||||
To normalize the children's peaks and valleys, so one child wasn't louder than the other, I used Audacity's **Normalize** effect.
|
||||
|
||||
![Normalize effect][9]
|
||||
|
||||
(Rich Lucente, [CC BY-SA 4.0][7])
|
||||
![Normalize effect][10]
|
||||
|
||||
It's important to understand that the Normalize and Amplify effects do very different things. Normalize adjusts the highest peaks and lowest valleys for multiple tracks, so they are all similar, whereas Amplify exaggerates the existing peaks and valleys. If I had used Amplify instead of Normalize, the louder child would have become even louder. I used the default settings to normalize the two audio tracks.
|
||||
|
||||
![Normalize defaults][10]
|
||||
|
||||
(Rich Lucente, [CC BY-SA 4.0][7])
|
||||
![Normalize defaults][11]
|
||||
|
||||
#### Remove background noise
|
||||
|
||||
Another thing I noticed is that there was noise between the spoken phrases on the tracks. Audacity has tooling to help reduce background noise and result in much cleaner audio. To reduce noise, select a sample of an audio track with background noise. I used the **View->Zoom** menu option to see the track's noise more easily.
|
||||
Another thing I noticed is that there was noise between the spoken phrases on the tracks. Audacity has tooling to help reduce background noise and result in much cleaner audio. To reduce noise, select a sample of an audio track with background noise. I used the **View->Zoom** menu option to see the track's noise more easily.
|
||||
|
||||
![Background noise sample][11]
|
||||
![Background noise sample][12]
|
||||
|
||||
(Rich Lucente, [CC BY-SA 4.0][7])
|
||||
To make sure I selected only the background noise, I listened to the selected audio clip using the **Play** button in the toolbar. Next, I selected **Effect->Noise Reduction**.
|
||||
|
||||
To make sure I selected only the background noise, I listened to the selected audio clip using the **Play** button in the toolbar. Next, I selected **Effect->Noise Reduction**.
|
||||
|
||||
![Noise Reduction effect][12]
|
||||
|
||||
(Rich Lucente, [CC BY-SA 4.0][7])
|
||||
![Noise Reduction effect][13]
|
||||
|
||||
Then I created a **Noise Profile** using step 1 in the **Noise Reduction** dialog.
|
||||
|
||||
![Get a Noise Profile from audio sample][13]
|
||||
|
||||
(Rich Lucente, [CC BY-SA 4.0][7])
|
||||
![Get a Noise Profile from audio sample][14]
|
||||
|
||||
Audacity characterizes the background noise in the audio sample so that it can be removed. To remove the background noise, I selected the entire audio track by pressing the small **Select** button to the left of the track.
|
||||
|
||||
![Select whole audio track button][14]
|
||||
|
||||
(Rich Lucente, [CC BY-SA 4.0][7])
|
||||
![Select whole audio track button][15]
|
||||
|
||||
I applied the **Noise Reduction** effect again, but this time I pressed **OK** in step 2 of the dialog. I accepted the default settings.
|
||||
|
||||
![Noise Reduction effect step 2][15]
|
||||
|
||||
(Rich Lucente, [CC BY-SA 4.0][7])
|
||||
![Noise Reduction effect step 2][16]
|
||||
|
||||
I repeated these steps for each child's audio track, so I had normalized audio tracks, and the background noise was characterized and removed.
|
||||
|
||||
@ -194,110 +194,106 @@ I repeated these steps for each child's audio track, so I had normalized audio t
|
||||
|
||||
The remaining task was to zoom and scroll through each track and export the specific clips as separate audio files in WAV format. When working with one child's track, I needed to mute the other tracks using either the small **Mute** button to the left of each audio track or, since there were so many tracks, selecting the **Solo** button for the track I wanted to work with.
|
||||
|
||||
![Mute and Solo buttons for multiple tracks][16]
|
||||
|
||||
(Rich Lucente, [CC BY-SA 4.0][7])
|
||||
![Mute and Solo buttons for multiple tracks][17]
|
||||
|
||||
Selecting each word and phrase can be tricky, but the ability to zoom into an audio track was my friend. I tried to set each audio clip's start and end to just before and just after the word or phrase being spoken. Before exporting any audio clips, I played the selected clip using the **Play** icon on the toolbar to make sure I got it all.
|
||||
|
||||
One interesting thing is how waveforms map to spoken words. The waveforms for "six" and "sixth" are incredibly similar, with the latter having a smaller audio waveform to the right for the "th" sound. I carefully tested each clip before exporting it to make sure I had captured the full word or phrase.
|
||||
|
||||
After selecting an audio clip for a word or phrase, I exported the selected audio using the **File->Export** menu.
|
||||
After selecting an audio clip for a word or phrase, I exported the selected audio using the **File->Export** menu.
|
||||
|
||||
![Exporting selected audio][17]
|
||||
|
||||
(Rich Lucente, [CC BY-SA 4.0][7])
|
||||
![Exporting selected audio][18]
|
||||
|
||||
I had to make sure to save each clip using the correct file name from the list of words and phrases. This is because the application I used to customize the voice assistant expects the file name to match an entry in the phrase list.
|
||||
|
||||
The expected file names for the audio clips (without the .wav extension) are listed below. Note the underscores within the phrases. If you're doing this project, adjust the bold file names to match your loved ones' nicknames and location preferences. You'll also have to make the same changes in the application source code.
|
||||
|
||||
_good
|
||||
morning
|
||||
afternoon
|
||||
evening
|
||||
night
|
||||
**nana_and_poppy**
|
||||
the_time
|
||||
today
|
||||
the_current_temperature_for
|
||||
**waynesboro
|
||||
ocean_city**
|
||||
is
|
||||
and
|
||||
degrees
|
||||
minus
|
||||
am
|
||||
pm
|
||||
january
|
||||
february
|
||||
march
|
||||
april
|
||||
may
|
||||
june
|
||||
july
|
||||
august
|
||||
september
|
||||
october_ | _november
|
||||
december
|
||||
first
|
||||
second
|
||||
third
|
||||
fourth
|
||||
fifth
|
||||
sixth
|
||||
seventh
|
||||
eighth
|
||||
ninth
|
||||
tenth
|
||||
eleventh
|
||||
twelfth
|
||||
thirteenth
|
||||
fourteenth
|
||||
fifteenth
|
||||
sixteenth
|
||||
seventeenth
|
||||
eighteenth
|
||||
nineteenth
|
||||
twentieth
|
||||
thirtieth
|
||||
oh
|
||||
one
|
||||
two_ | _three
|
||||
four
|
||||
five
|
||||
six
|
||||
seven
|
||||
eight
|
||||
nine
|
||||
ten
|
||||
eleven
|
||||
twelve
|
||||
thirteen
|
||||
fourteen
|
||||
fifteen
|
||||
sixteen
|
||||
seventeen
|
||||
eighteen
|
||||
nineteen
|
||||
twenty
|
||||
thirty
|
||||
forty
|
||||
fifty
|
||||
sixty
|
||||
seventy
|
||||
eighty
|
||||
ninety
|
||||
hundred_
|
||||
---|---|---
|
||||
(这个表格有问题,待修正)
|
||||
| - | - | - |
|
||||
| :- | :- | :- |
|
||||
| good
|
||||
morning
|
||||
afternoon
|
||||
evening
|
||||
night
|
||||
nana_and_poppy
|
||||
the_time
|
||||
today
|
||||
the_current_temperature_for
|
||||
waynesboro
|
||||
ocean_city
|
||||
is
|
||||
and
|
||||
degrees
|
||||
minus
|
||||
am
|
||||
pm
|
||||
january
|
||||
february
|
||||
march
|
||||
april
|
||||
may
|
||||
june
|
||||
july
|
||||
august
|
||||
september
|
||||
october | november
|
||||
december
|
||||
first
|
||||
second
|
||||
third
|
||||
fourth
|
||||
fifth
|
||||
sixth
|
||||
seventh
|
||||
eighth
|
||||
ninth
|
||||
tenth
|
||||
eleventh
|
||||
twelfth
|
||||
thirteenth
|
||||
fourteenth
|
||||
fifteenth
|
||||
sixteenth
|
||||
seventeenth
|
||||
eighteenth
|
||||
nineteenth
|
||||
twentieth
|
||||
thirtieth
|
||||
oh
|
||||
one
|
||||
two | three
|
||||
four
|
||||
five
|
||||
six
|
||||
seven
|
||||
eight
|
||||
nine
|
||||
ten
|
||||
eleven
|
||||
twelve
|
||||
thirteen
|
||||
fourteen
|
||||
fifteen
|
||||
sixteen
|
||||
seventeen
|
||||
eighteen
|
||||
nineteen
|
||||
twenty
|
||||
thirty
|
||||
forty
|
||||
fifty
|
||||
sixty
|
||||
seventy
|
||||
eighty
|
||||
ninety
|
||||
hundred |
|
||||
|
||||
This project's GitHub repository also includes a Bash script to run as a sanity check for any missing or misnamed files.
|
||||
|
||||
After choosing each clip's appropriate name, I saved the clip in the child's specific folder (child1, child2, etc.) as a WAV format file. I accepted the default export settings.
|
||||
|
||||
![Converting clip to WAV format][18]
|
||||
|
||||
(Rich Lucente, [CC BY-SA 4.0][7])
|
||||
![Converting clip to WAV format][19]
|
||||
|
||||
After exporting all the audio clips, I had a folder for each child that was fully populated with WAV files for the phrases above. This seems like a lot of work, but it took only about 90 minutes for each child, and I got way more efficient with each successive audio clip.
|
||||
|
||||
@ -305,22 +301,19 @@ After exporting all the audio clips, I had a folder for each child that was full
|
||||
|
||||
Now that I had the audio clips for the greeting, I needed to think about the application and how to package it. I also wanted an open source-friendly solution that was open to modification.
|
||||
|
||||
About two years ago, a colleague gave me a [Google AIY Voice Kit][19] that he grabbed from the clearance bin for just $10. It's a cleverly folded box containing a speaker, microphone, and custom circuit board. You supply a Raspberry Pi and quickly have a do-it-yourself Google voice assistant. These kits are available for purchase online and in electronics stores. This small box offered an easy way to package the project.
|
||||
About two years ago, a colleague gave me a [Google AIY Voice Kit][20] that he grabbed from the clearance bin for just $10. It's a cleverly folded box containing a speaker, microphone, and custom circuit board. You supply a Raspberry Pi and quickly have a do-it-yourself Google voice assistant. These kits are available for purchase online and in electronics stores. This small box offered an easy way to package the project.
|
||||
|
||||
![Google AIY Voice Kit][20]
|
||||
|
||||
(Rich Lucente, [CC BY-SA 4.0][7])
|
||||
![Google AIY Voice Kit][21]
|
||||
|
||||
### Customize the voice assistant
|
||||
|
||||
The Google kit includes a Python API and several Python modules. I followed the kit's instructions to get the initial configuration working. The [Google Assistant gRPC][21] software is open source under an Apache 2.0 license.
|
||||
The Google kit includes a Python API and several Python modules. I followed the kit's instructions to get the initial configuration working. The [Google Assistant gRPC][22] software is open source under an Apache 2.0 license.
|
||||
|
||||
I adapted the Google Assistant gRPC demo to implement my application. The application's operation is fairly simple: First, it waits for the device's button to be pressed. The code then constructs four separate word lists for: 1. the greeting and date, 2. the current time, 3. the current temperature of the first location, and 4. the current temperature of the second location. The children's voices are randomly shuffled, and then each word list is used to play the audio clips corresponding to the child assigned to that list. (This is why it was important to strictly follow the naming convention for the audio clips.) The application then initiates a conversation with the Google Assistant API.
|
||||
|
||||
At first, I thought the code to gather weather data for the current temperature and convert numbers to words would be challenging. This proved not to be the case at all. In fact, existing open source Python modules made it all simple and intuitive.
|
||||
|
||||
There were two cases to be addressed for converting numbers to word lists: I needed to convert ordinal numbers to words (e.g., 1 and 2 to first and second), and I also needed to convert cardinal numbers to words (e.g., 28 to twenty-eight). The open source [inflect.py module][22] has functions that handle both cases quite easily.
|
||||
|
||||
There were two cases to be addressed for converting numbers to word lists: I needed to convert ordinal numbers to words (e.g., 1 and 2 to first and second), and I also needed to convert cardinal numbers to words (e.g., 28 to twenty-eight). The open source [inflect.py module][23] has functions that handle both cases quite easily.
|
||||
|
||||
```
|
||||
import inflect
|
||||
@ -337,8 +330,7 @@ print(p.number_to_words(p.ordinal(number)).replace('-', ' ').split(' '))
|
||||
|
||||
The inflect engine returns string representations of the numbers with embedded hyphens (e.g., twenty-three) so that the code splits the strings into variable-length word lists by converting the hyphens to spaces and splitting the string into a list using a space as the delimiter.
|
||||
|
||||
The next problem to solve was getting the current temperature for the two locations. [Open Weather Map][23] offers a free-tier weather service that allows up to 60 calls a minute or 1 million calls a month, which is way more than this project needs. I signed up for the free-tier service and received an API key. It was very easy to access the service by using the open source Python wrapper module [PyOWM][24]. Here is a simplified code snippet:
|
||||
|
||||
The next problem to solve was getting the current temperature for the two locations. [Open Weather Map][24] offers a free-tier weather service that allows up to 60 calls a minute or 1 million calls a month, which is way more than this project needs. I signed up for the free-tier service and received an API key. It was very easy to access the service by using the open source Python wrapper module [PyOWM][25]. Here is a simplified code snippet:
|
||||
|
||||
```
|
||||
import pyowm
|
||||
@ -359,46 +351,49 @@ temp = round(observation.weather.temperature('fahrenheit')['temp'])
|
||||
|
||||
### Wrapping it up with a bow
|
||||
|
||||
The full source code for the project is available in my [GitHub repository][25]. The project includes a systemd service unit file adapted from Google's demo to automatically start the application on device boot. The GitHub repository includes instructions to install the Python modules and configure the systemd service.
|
||||
The full source code for the project is available in my [GitHub repository][26]. The project includes a systemd service unit file adapted from Google's demo to automatically start the application on device boot. The GitHub repository includes instructions to install the Python modules and configure the systemd service.
|
||||
|
||||
I created a [short video][26] of the result. Five custom voice assistants were distributed during the holidays: one each for the great grandparents and grandparents of each child. For some, these gifts brought tears of joy. The children's voices are absolutely adorable and these boxes capture a fleeting moment of childhood that can be enjoyed for a very long time.
|
||||
I created a [short video][27] of the result. Five custom voice assistants were distributed during the holidays: one each for the great grandparents and grandparents of each child. For some, these gifts brought tears of joy. The children's voices are absolutely adorable and these boxes capture a fleeting moment of childhood that can be enjoyed for a very long time.
|
||||
|
||||
Image by: (Rich Lucente, CC BY-SA 4.0)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/1/customize-voice-assistant
|
||||
|
||||
作者:[Rich Lucente][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][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/rlucente
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/sound-radio-noise-communication.png?itok=KMNn9QrZ (radio communication signals)
|
||||
[2]: https://opensource.com/article/20/7/mycroft-voice-skill
|
||||
[3]: https://en.wikipedia.org/wiki/WAV
|
||||
[4]: https://www.audacityteam.org/
|
||||
[5]: https://www.audacityteam.org/download/
|
||||
[6]: https://opensource.com/sites/default/files/uploads/audacity1_importaudio.png (Import audio files in Audacity)
|
||||
[7]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[8]: https://opensource.com/sites/default/files/uploads/audacity2_selectingtracks.png (Selecting all the audio tracks)
|
||||
[9]: https://opensource.com/sites/default/files/uploads/audacity3_normalize.png (Normalize effect)
|
||||
[10]: https://opensource.com/sites/default/files/uploads/audacity4_normalizedefaults.png (Normalize defaults)
|
||||
[11]: https://opensource.com/sites/default/files/uploads/audacity5_backgroundnoise.png (Background noise sample)
|
||||
[12]: https://opensource.com/sites/default/files/uploads/audacity6_noisereduction.png (Noise Reduction effect)
|
||||
[13]: https://opensource.com/sites/default/files/uploads/audacity7_noiseprofile.png (Get a Noise Profile from audio sample)
|
||||
[14]: https://opensource.com/sites/default/files/uploads/audacity8_selecttrack.png (Select whole audio track button)
|
||||
[15]: https://opensource.com/sites/default/files/uploads/audacity9_noisereduction2.png (Noise Reduction effect step 2)
|
||||
[16]: https://opensource.com/sites/default/files/uploads/audacity10_mutesolo.png (Mute and Solo buttons for multiple tracks)
|
||||
[17]: https://opensource.com/sites/default/files/uploads/audacity11_exportaudio.png (Exporting selected audio)
|
||||
[18]: https://opensource.com/sites/default/files/uploads/audacity12_convertwav.png (Converting clip to WAV format)
|
||||
[19]: https://aiyprojects.withgoogle.com/voice/
|
||||
[20]: https://opensource.com/sites/default/files/uploads/googleaiy.png (Google AIY Voice Kit)
|
||||
[21]: https://pypi.org/project/google-assistant-grpc/
|
||||
[22]: https://pypi.org/project/inflect
|
||||
[23]: https://openweathermap.org/
|
||||
[24]: https://pypi.org/project/pyowm
|
||||
[25]: https://github.com/rlucente-se-jboss/nana-poppy-project
|
||||
[26]: https://youtu.be/Co7rigJRNUM
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/sound-radio-noise-communication.png
|
||||
[2]: https://www.flickr.com/photos/internetarchivebookimages/14571450820/in/photolist-ocCuEG-otg1AX-hPy8JE-oc9YmN-oeUU2C-8cKWej-hQz72S-rpae2k-ocNYbT-oxbPTB-odGRsQ-ouDBo1-i5GTL8-qscJfA-idDrfk-i5D6oK-6K6iNH-ouxpn7-i8SivQ-oeY1eG-i7HGbT-bqXPhH-hN5on7-i9Q8YD-ouFYDw-fpy7Lo-oeSJo1-otqUu4-hNaVhf-oydqAV-owur2M-owkTSD-oydSWR-ocayce-ovFdYk-ocdaeL-ouE9UP-zmmrhp-qxtozB-ouqnSQ-obYbwS-odrnXt-ousXXw-ocA6Uo-owme9S-ouACY2-ocajY1-oeUJQG-ouryBk-ouxMJb
|
||||
[3]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[4]: https://opensource.com/article/20/7/mycroft-voice-skill
|
||||
[5]: https://en.wikipedia.org/wiki/WAV
|
||||
[6]: https://www.audacityteam.org/
|
||||
[7]: https://www.audacityteam.org/download/
|
||||
[8]: https://opensource.com/sites/default/files/uploads/audacity1_importaudio.png
|
||||
[9]: https://opensource.com/sites/default/files/uploads/audacity2_selectingtracks.png
|
||||
[10]: https://opensource.com/sites/default/files/uploads/audacity3_normalize.png
|
||||
[11]: https://opensource.com/sites/default/files/uploads/audacity4_normalizedefaults.png
|
||||
[12]: https://opensource.com/sites/default/files/uploads/audacity5_backgroundnoise.png
|
||||
[13]: https://opensource.com/sites/default/files/uploads/audacity6_noisereduction.png
|
||||
[14]: https://opensource.com/sites/default/files/uploads/audacity7_noiseprofile.png
|
||||
[15]: https://opensource.com/sites/default/files/uploads/audacity8_selecttrack.png
|
||||
[16]: https://opensource.com/sites/default/files/uploads/audacity9_noisereduction2.png
|
||||
[17]: https://opensource.com/sites/default/files/uploads/audacity10_mutesolo.png
|
||||
[18]: https://opensource.com/sites/default/files/uploads/audacity11_exportaudio.png
|
||||
[19]: https://opensource.com/sites/default/files/uploads/audacity12_convertwav.png
|
||||
[20]: https://aiyprojects.withgoogle.com/voice/
|
||||
[21]: https://opensource.com/sites/default/files/uploads/googleaiy.png
|
||||
[22]: https://pypi.org/project/google-assistant-grpc/
|
||||
[23]: https://pypi.org/project/inflect
|
||||
[24]: https://openweathermap.org/
|
||||
[25]: https://pypi.org/project/pyowm
|
||||
[26]: https://github.com/rlucente-se-jboss/nana-poppy-project
|
||||
[27]: https://youtu.be/Co7rigJRNUM
|
||||
|
@ -1,27 +1,28 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (A hands-on tutorial for using the GNU Project Debugger)
|
||||
[#]: via: (https://opensource.com/article/21/1/gnu-project-debugger)
|
||||
[#]: author: (Stephan Avenwedde https://opensource.com/users/hansic99)
|
||||
[#]: subject: "A hands-on tutorial for using the GNU Project Debugger"
|
||||
[#]: via: "https://opensource.com/article/21/1/gnu-project-debugger"
|
||||
[#]: author: "Stephan Avenwedde https://opensource.com/users/hansic99"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
A hands-on tutorial for using the GNU Project Debugger
|
||||
======
|
||||
The GNU Project Debugger is a powerful tool for finding bugs in
|
||||
programs.
|
||||
The GNU Project Debugger is a powerful tool for finding bugs in programs.
|
||||
|
||||
![magnifying glass on computer screen, finding a bug in the code][1]
|
||||
|
||||
Image by: Opensource.com
|
||||
|
||||
If you're a programmer and you want to put a certain functionality in your software, you start by thinking of ways to implement it—such as writing a method, defining a class, or creating new data types. Then you write the implementation in a language that the compiler or interpreter can understand. But what if the compiler or interpreter does not understand the instructions as you had them in mind, even though you're sure you did everything right? What if the software works fine most of the time but causes bugs in certain circumstances? In these cases, you have to know how to use a debugger correctly to find the source of your troubles.
|
||||
|
||||
The GNU Project Debugger ([GDB][2]) is a powerful tool for finding bugs in programs. It helps you uncover the reason for an error or crash by tracking what is going on inside the program during execution.
|
||||
|
||||
This article is a hands-on tutorial on basic GDB usage. To follow along with the examples, open the command line and clone this repository:
|
||||
|
||||
|
||||
```
|
||||
`git clone https://github.com/hANSIc99/core_dump_example.git`
|
||||
git clone https://github.com/hANSIc99/core_dump_example.git
|
||||
```
|
||||
|
||||
### Shortcuts
|
||||
@ -30,7 +31,7 @@ Every command in GDB can be shortened. For example, `info break`, which shows th
|
||||
|
||||
### Command-line parameters
|
||||
|
||||
You can attach GDB to every executable. Navigate to the repository you cloned, and compile it by running `make`. You should now have an executable called **coredump**. (See my article on [_Creating and debugging Linux dump files_][3] for more information..
|
||||
You can attach GDB to every executable. Navigate to the repository you cloned, and compile it by running `make`. You should now have an executable called **coredump**. (See my article on [Creating and debugging Linux dump files][3] for more information..
|
||||
|
||||
To attach GDB to the executable, type: `gdb coredump`.
|
||||
|
||||
@ -38,87 +39,66 @@ Your output should look like this:
|
||||
|
||||
![gdb coredump output][4]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
It says no debugging symbols were found.
|
||||
|
||||
Debugging information is part of the object file (the executable) and includes data types, function signatures, and the relationship between the source code and the opcode. At this point, you have two options:
|
||||
|
||||
* Continue debugging the assembly (see "[Debug without symbols][6]" below)
|
||||
* Compile with debug information using the information in the next section
|
||||
|
||||
|
||||
* Continue debugging the assembly (see "Debug without symbols" below)
|
||||
* Compile with debug information using the information in the next section
|
||||
|
||||
### Compile with debug information
|
||||
|
||||
To include debug information in the binary file, you have to recompile it. Open the **Makefile** and remove the hashtag (`#`) from line 9:
|
||||
|
||||
To include debug information in the binary file, you have to recompile it. Open the **Makefile** and remove the hashtag (`#` ) from line 9:
|
||||
|
||||
```
|
||||
`CFLAGS =-Wall -Werror -std=c++11 -g`
|
||||
CFLAGS =-Wall -Werror -std=c++11 -g
|
||||
```
|
||||
|
||||
The `g` option tells the compiler to include the debug information. Run `make clean` followed by `make` and invoke GDB again. You should get this output and can start debugging the code:
|
||||
|
||||
![GDB output with symbols][7]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![GDB output with symbols][5]
|
||||
|
||||
The additional debugging information will increase the size of the executable. In this case, it increases the executable by 2.5 times (from 26,088 byte to 65,480 byte).
|
||||
|
||||
Start the program with the `-c1` switch by typing `run -c1`. The program will start and crash when it reaches `State_4`:
|
||||
Start the program with the `-c1` switch by typing `run -c1`. The program will start and crash when it reaches `State_4` :
|
||||
|
||||
![gdb output crash on c1 switch][8]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![gdb output crash on c1 switch][6]
|
||||
|
||||
You can retrieve additional information about the program. The command `info source` provides information about the current file:
|
||||
|
||||
![gdb info source output][9]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
* 101 lines
|
||||
* Language: C++
|
||||
* Compiler (version, tuning, architecture, debug flag, language standard)
|
||||
* Debugging format: [DWARF 2][10]
|
||||
* No preprocessor macro information available (when compiled with GCC, macros are available only when [compiled with the `-g3` flag][11]).
|
||||
![gdb info source output][7]
|
||||
|
||||
|
||||
* 101 lines
|
||||
* Language: C++
|
||||
* Compiler (version, tuning, architecture, debug flag, language standard)
|
||||
* Debugging format: [DWARF 2][8]
|
||||
* No preprocessor macro information available (when compiled with GCC, macros are available only when [compiled with the `-g3` flag][9]).
|
||||
|
||||
The command `info shared` prints a list of dynamic libraries with their addresses in the virtual address space that was loaded on startup so that the program will execute:
|
||||
|
||||
![gdb info shared output][12]
|
||||
![gdb info shared output][10]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
If you want to learn about library handling in Linux, see my article [_How to handle dynamic and static libraries in Linux_][13].
|
||||
If you want to learn about library handling in Linux, see my article [How to handle dynamic and static libraries in Linux][11].
|
||||
|
||||
### Debug the program
|
||||
|
||||
You may have noticed that you can start the program inside GDB with the `run` command. The `run` command accepts command-line arguments like you would use to start the program from the console. The `-c1` switch will cause the program to crash on stage 4. To run the program from the beginning, you don't have to quit GDB; simply use the `run` command again. Without the `-c1` switch, the program executes an infinite loop. You would have to stop it with **Ctrl+C**.
|
||||
|
||||
![gdb output stopped by sigint][14]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![gdb output stopped by sigint][12]
|
||||
|
||||
You can also execute a program step by step. In C/C++, the entry point is the `main` function. Use the command `list main` to open the part of the source code that shows the `main` function:
|
||||
|
||||
![gdb output list main][15]
|
||||
![gdb output list main][13]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
The `main` function is on line 33, so add a breakpoint there by typing `break 33` :
|
||||
|
||||
The `main` function is on line 33, so add a breakpoint there by typing `break 33`:
|
||||
|
||||
![gdb output breakpoint added][16]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![gdb output breakpoint added][14]
|
||||
|
||||
Run the program by typing `run`. As expected, the program stops at the `main` function. Type `layout src` to show the source code in parallel:
|
||||
|
||||
![gdb output break at main][17]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![gdb output break at main][15]
|
||||
|
||||
You are now in GDB's text user interface (TUI) mode. Use the Up and Down arrow keys to scroll through the source code.
|
||||
|
||||
@ -126,13 +106,11 @@ GDB highlights the line to be executed. By typing `next` (n), you can execute th
|
||||
|
||||
From time to time, you will notice that TUI's output gets a bit corrupted:
|
||||
|
||||
![gdb output corrupted][18]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![gdb output corrupted][16]
|
||||
|
||||
If this happens, press **Ctrl+L** to reset the screen.
|
||||
|
||||
Use **Ctrl+X+A** to enter and leave TUI mode at will. You can find [other key bindings][19] in the manual.
|
||||
Use **Ctrl+X+A** to enter and leave TUI mode at will. You can find [other key bindings][17] in the manual.
|
||||
|
||||
To quit GDB, simply type `quit`.
|
||||
|
||||
@ -140,45 +118,39 @@ To quit GDB, simply type `quit`.
|
||||
|
||||
The heart of this example program consists of a state machine running in an infinite loop. The variable `n_state` is a simple enum that determines the current state:
|
||||
|
||||
|
||||
```
|
||||
while(true){
|
||||
switch(n_state){
|
||||
case State_1:
|
||||
std::cout << "State_1 reached" << std::flush;
|
||||
std::cout << "State_1 reached" << std::flush;
|
||||
n_state = State_2;
|
||||
break;
|
||||
case State_2:
|
||||
std::cout << "State_2 reached" << std::flush;
|
||||
std::cout << "State_2 reached" << std::flush;
|
||||
n_state = State_3;
|
||||
break;
|
||||
|
||||
|
||||
(.....)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You want to stop the program when `n_state` is set to the value `State_5`. To do so, stop the program at the `main` function and set a watchpoint for `n_state`:
|
||||
|
||||
You want to stop the program when `n_state` is set to the value `State_5`. To do so, stop the program at the `main` function and set a watchpoint for `n_state` :
|
||||
|
||||
```
|
||||
`watch n_state == State_5`
|
||||
watch n_state == State_5
|
||||
```
|
||||
|
||||
Setting watchpoints with the variable name works only if the desired variable is available in the current context.
|
||||
|
||||
When you continue the program's execution by typing `continue`, you should get output like:
|
||||
|
||||
![gdb output stop on watchpoint_1][20]
|
||||
![gdb output stop on watchpoint_1][18]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
If you continue the execution, GDB will stop when the watchpoint expression evaluates to `false` :
|
||||
|
||||
If you continue the execution, GDB will stop when the watchpoint expression evaluates to `false`:
|
||||
|
||||
![gdb output stop on watchpoint_2][21]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![gdb output stop on watchpoint_2][19]
|
||||
|
||||
You can specify watchpoints for general value changes, specific values, and read or write access.
|
||||
|
||||
@ -186,21 +158,17 @@ You can specify watchpoints for general value changes, specific values, and read
|
||||
|
||||
Type `info watchpoints` to print a list of previously set watchpoints:
|
||||
|
||||
![gdb output info watchpoints][22]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![gdb output info watchpoints][20]
|
||||
|
||||
#### Delete breakpoints and watchpoints
|
||||
|
||||
As you can see, watchpoints are numbers. To delete a specific watchpoint, type `delete` followed by the number of the watchpoint. For example, my watchpoint has the number 2; to remove this watchpoint, enter `delete 2`.
|
||||
|
||||
_Caution:_ If you use `delete` without specifying a number, _all_ watchpoints and breakpoints will be deleted.
|
||||
*Caution:* If you use `delete` without specifying a number, *all* watchpoints and breakpoints will be deleted.
|
||||
|
||||
The same applies to breakpoints. In the screenshot below, I added several breakpoints and printed a list of them by typing `info breakpoint`:
|
||||
The same applies to breakpoints. In the screenshot below, I added several breakpoints and printed a list of them by typing `info breakpoint` :
|
||||
|
||||
![gdb output info breakpoints][23]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![gdb output info breakpoints][21]
|
||||
|
||||
To remove a single breakpoint, type `delete` followed by its number. Alternatively, you can remove a breakpoint by specifying its line number. For example, the command `clear 78` will remove breakpoint number 7, which is set on line 78.
|
||||
|
||||
@ -208,9 +176,7 @@ To remove a single breakpoint, type `delete` followed by its number. Alternative
|
||||
|
||||
Instead of removing a breakpoint or watchpoint, you can disable it by typing `disable` followed by its number. In the following, breakpoints 3 and 4 are disabled and are marked with a minus sign in the code window:
|
||||
|
||||
![disabled breakpoints][24]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![disabled breakpoints][22]
|
||||
|
||||
It is also possible to modify a range of breakpoints or watchpoints by typing something like `disable 2 - 4`. If you want to reactivate the points, type `enable` followed by their numbers.
|
||||
|
||||
@ -224,40 +190,31 @@ The `main` function includes the variable `n_state_3_count`, which is incremente
|
||||
|
||||
To add a conditional breakpoint based on the value of `n_state_3_count` type:
|
||||
|
||||
|
||||
```
|
||||
`break 54 if n_state_3_count == 3`
|
||||
break 54 if n_state_3_count == 3
|
||||
```
|
||||
|
||||
![Set conditional breakpoint][25]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![Set conditional breakpoint][23]
|
||||
|
||||
Continue the execution. The program will execute the state machine three times before it stops at line 54. To check the value of `n_state_3_count`, type:
|
||||
|
||||
|
||||
```
|
||||
`print n_state_3_count`
|
||||
print n_state_3_count
|
||||
```
|
||||
|
||||
![print variable][26]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![print variable][24]
|
||||
|
||||
#### Make breakpoints conditional
|
||||
|
||||
It is also possible to make an existing breakpoint conditional. Remove the recently added breakpoint with `clear 54`, and add a simple breakpoint by typing `break 54`. You can make this breakpoint conditional by typing:
|
||||
|
||||
|
||||
```
|
||||
`condition 3 n_state_3_count == 9`
|
||||
condition 3 n_state_3_count == 9
|
||||
```
|
||||
|
||||
The `3` refers to the breakpoint number.
|
||||
|
||||
![modify breakpoint][27]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![modify breakpoint][25]
|
||||
|
||||
#### Set breakpoints in other source files
|
||||
|
||||
@ -269,143 +226,109 @@ In addition to breakpoints and watchpoints, you can also set catchpoints. Catchp
|
||||
|
||||
To catch the `write` syscall, which is used to write to STDOUT, enter:
|
||||
|
||||
|
||||
```
|
||||
`catch syscall write`
|
||||
catch syscall write
|
||||
```
|
||||
|
||||
![catch syscall write output][28]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![catch syscall write output][26]
|
||||
|
||||
Each time the program writes to the console output, GDB will interrupt execution.
|
||||
|
||||
In the manual, you can find a whole chapter [covering break-, watch-, and catchpoints][29].
|
||||
In the manual, you can find a whole chapter [covering break-, watch-, and catchpoints][27].
|
||||
|
||||
### Evaluate and manipulate symbols
|
||||
|
||||
Printing the values of variables is done with the `print` command. The general syntax is `print <expression> <value>`. The value of a variable can be modified by typing:
|
||||
|
||||
|
||||
```
|
||||
`set variable <variable-name> <new-value>.`
|
||||
set variable <variable-name> <new-value>.
|
||||
```
|
||||
|
||||
In the screenshot below, I gave the variable `n_state_3_count` the value _123_.
|
||||
In the screenshot below, I gave the variable `n_state_3_count` the value *123*.
|
||||
|
||||
![print variable][30]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![catch syscall write output][28]
|
||||
|
||||
The `/x` expression prints the value in hexadecimal; with the `&` operator, you can print the address within the virtual address space.
|
||||
|
||||
If you are not sure of a certain symbol's data type, you can find it with `whatis`:
|
||||
If you are not sure of a certain symbol's data type, you can find it with `whatis` :
|
||||
|
||||
![whatis output][31]
|
||||
![whatis output][29]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
If you want to list all variables that are available in the scope of the `main` function, type `info scope main` :
|
||||
|
||||
If you want to list all variables that are available in the scope of the `main` function, type `info scope main`:
|
||||
|
||||
![info scope main output][32]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![info scope main output][30]
|
||||
|
||||
The `DW_OP_fbreg` values refer to the stack offset based on the current subroutine.
|
||||
|
||||
Alternatively, if you are already inside a function and want to list all variables on the current stack frame, you can use `info locals`:
|
||||
Alternatively, if you are already inside a function and want to list all variables on the current stack frame, you can use `info locals` :
|
||||
|
||||
![info locals output][33]
|
||||
![info locals output][31]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
Check the manual to learn more about [examining symbols][34].
|
||||
Check the manual to learn more about [examining symbols][32].
|
||||
|
||||
### Attach to a running process
|
||||
|
||||
The command `gdb attach <process-id>` allows you to attach to an already running process by specifying the process ID (PID). Luckily, the `coredump` program prints its current PID to the screen, so you don't have to manually find it with [ps][35] or [top][36].
|
||||
The command `gdb attach <process-id>` allows you to attach to an already running process by specifying the process ID (PID). Luckily, the `coredump` program prints its current PID to the screen, so you don't have to manually find it with [ps][33] or [top][34].
|
||||
|
||||
Start an instance of the coredump application:
|
||||
|
||||
|
||||
```
|
||||
`./coredump`
|
||||
./coredump
|
||||
```
|
||||
|
||||
![coredump application][37]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![coredump application][35]
|
||||
|
||||
The operating system gives the PID `2849`. Open a separate console window, move to the coredump application's source directory, and attach GDB:
|
||||
|
||||
|
||||
```
|
||||
`gdb attach 2849`
|
||||
gdb attach 2849
|
||||
```
|
||||
|
||||
![attach GDB to coredump][38]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![attach GDB to coredump][36]
|
||||
|
||||
GDB immediately stops the execution when you attach it. Type `layout src` and `backtrace` to examine the call stack:
|
||||
|
||||
![layout src and backtrace output][39]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![layout src and backtrace output][37]
|
||||
|
||||
The output shows the process interrupted while executing the `std::this_thread::sleep_for<...>(...)` function that was called in line 92 of `main.cpp`.
|
||||
|
||||
As soon as you quit GDB, the process will continue running.
|
||||
|
||||
You can find more information about [attaching to a running process][40] in the GDB manual.
|
||||
You can find more information about [attaching to a running process][38] in the GDB manual.
|
||||
|
||||
#### Move through the stack
|
||||
|
||||
Return to the program by using `up` two times to move up in the stack to `main.cpp`:
|
||||
Return to the program by using `up` two times to move up in the stack to `main.cpp` :
|
||||
|
||||
![moving up the stack to main.cpp][41]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
![moving up the stack to main.cpp][39]
|
||||
|
||||
Usually, the compiler will create a subroutine for each function or method. Each subroutine has its own stack frame, so moving upwards in the stackframe means moving upwards in the callstack.
|
||||
|
||||
You can find out more about [stack evaluation][42] in the manual.
|
||||
You can find out more about [stack evaluation][40] in the manual.
|
||||
|
||||
#### Specify the source files
|
||||
|
||||
When attaching to an already running process, GDB will look for the source files in the current working directory. Alternatively, you can specify the source directories manually with the [`directory` command][43].
|
||||
When attaching to an already running process, GDB will look for the source files in the current working directory. Alternatively, you can specify the source directories manually with the [directory command][41].
|
||||
|
||||
### Evaluate dump files
|
||||
|
||||
Read [_Creating and debugging Linux dump files_][3] for information about this topic.
|
||||
Read [Creating and debugging Linux dump files][42] for information about this topic.
|
||||
|
||||
TL;DR:
|
||||
|
||||
1. I assume you're working with a recent version of Fedora
|
||||
|
||||
2. Invoke coredump with the c1 switch: `coredump -c1`
|
||||
1. I assume you're working with a recent version of Fedora
|
||||
2. Invoke coredump with the c1 switch: `coredump -c1`
|
||||
3. Load the latest dumpfile with GDB: `coredumpctl debug`
|
||||
4. Open TUI mode and enter `layout src`
|
||||
|
||||
![Crash meme][44]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
3. Load the latest dumpfile with GDB: `coredumpctl debug`
|
||||
|
||||
4. Open TUI mode and enter `layout src`
|
||||
|
||||
|
||||
|
||||
|
||||
![coredump output][45]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
The output of `backtrace` shows that the crash happened five stack frames away from `main.cpp`. Enter to jump directly to the faulty line of code in `main.cpp`:
|
||||
The output of `backtrace` shows that the crash happened five stack frames away from `main.cpp`. Enter to jump directly to the faulty line of code in `main.cpp` :
|
||||
|
||||
![up 5 output][46]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
A look at the source code shows that the program tried to free a pointer that was not returned by a memory management function. This results in undefined behavior and caused the `SIGABRT`.
|
||||
|
||||
### Debug without symbols
|
||||
@ -416,56 +339,43 @@ Check out how it works with this example.
|
||||
|
||||
Go to the source directory, open the **Makefile**, and edit line 9 like this:
|
||||
|
||||
|
||||
```
|
||||
`CFLAGS =-Wall -Werror -std=c++11 #-g`
|
||||
CFLAGS =-Wall -Werror -std=c++11 #-g
|
||||
```
|
||||
|
||||
To recompile the program, run `make clean` followed by `make` and start GDB. The program no longer has any debugging symbols to lead the way through the source code.
|
||||
To recompile the program, run `make clean` followed by `make` and start GDB. The program no longer has any debugging symbols to lead the way through the source code.
|
||||
|
||||
![no debugging symbols][48]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
The command `info file` reveals the memory areas and entry point of the binary:
|
||||
|
||||
![info file output][49]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
The entry point corresponds with the beginning of the `.text` area, which contains the actual opcode. To add a breakpoint at the entry point, type `break *0x401110` then start execution by typing `run`:
|
||||
The entry point corresponds with the beginning of the `.text` area, which contains the actual opcode. To add a breakpoint at the entry point, type `break *0x401110` then start execution by typing `run` :
|
||||
|
||||
![breakpoint at the entry point][50]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
To set up a breakpoint at a certain address, specify it with the dereferencing operator `*`.
|
||||
|
||||
#### Choose the disassembler flavor
|
||||
|
||||
Before digging deeper into assembly, you can choose which [assembly flavor][51] to use. GDB's default is AT&T, but I prefer the Intel syntax. Change it with:
|
||||
|
||||
Before digging deeper into assembly, you can choose which [assembly flavor][51] to use. GDB's default is AT&T, but I prefer the Intel syntax. Change it with:
|
||||
|
||||
```
|
||||
`set disassembly-flavor intel`
|
||||
set disassembly-flavor intel
|
||||
```
|
||||
|
||||
![changing assembly flavor][52]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
Now open the assembly and register the window by typing `layout asm` and `layout reg`. You should now see output like this:
|
||||
|
||||
![layout asm and layout reg output][53]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
#### Save configuration files
|
||||
|
||||
Although you have already entered many commands, you haven't actually started debugging. If you are heavily debugging an application or trying to solve a reverse-engineering challenge, it can be useful to save your GDB-specific settings in a file.
|
||||
|
||||
The [config file `gdbinit`][54] in this project's GitHub repository contains the recently used commands:
|
||||
|
||||
The [config file gdbinit][54] in this project's GitHub repository contains the recently used commands:
|
||||
|
||||
```
|
||||
set disassembly-flavor intel
|
||||
@ -486,11 +396,9 @@ With the `c2` switch applied, the program will crash. The program stops at the e
|
||||
|
||||
![continuing execution after crash][55]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
The `idiv` instruction performs an integer division with the dividend in the `RAX` register and the divisor specified as an argument. The quotient is loaded into the `RAX` register, and the remainder is loaded into `RDX`.
|
||||
|
||||
From the register overview, you can see the `RAX` contains _5_, so you have to find out which value is stored on the stack at position `RBP-0x4`.
|
||||
From the register overview, you can see the `RAX` contains *5*, so you have to find out which value is stored on the stack at position `RBP-0x4`.
|
||||
|
||||
#### Read memory
|
||||
|
||||
@ -498,71 +406,58 @@ To read raw memory content, you must specify a few more parameters than for read
|
||||
|
||||
![stack division output][56]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
You're most interested in the value of `rbp-0x4` because this is the position where the argument for `idiv` is stored. From the screenshot, you can see that the next variable is located at `rbp-0x8`, so the variable at `rbp-0x4` is 4 bytes wide.
|
||||
|
||||
In GDB, you can use the `x` command to _examine_ any memory content:
|
||||
In GDB, you can use the `x` command to *examine* any memory content:
|
||||
|
||||
> `x/` < optional parameter `n` `f` `u` > < memory address `addr` >
|
||||
> `x/` < optional parameter `n` `f` `u` > < memory address `addr` >
|
||||
|
||||
Optional parameters:
|
||||
|
||||
* `n`: Repeat count (default: 1) refers to the unit size
|
||||
* `f`: Format specifier, like in [printf][57]
|
||||
* `u`: Unit size
|
||||
* `b`: bytes
|
||||
* `h`: half words (2 bytes)
|
||||
* `w`: word (4 bytes)(default)
|
||||
* `g`: giant word (8 bytes)
|
||||
* n: Repeat count (default: 1) refers to the unit size
|
||||
* f: Format specifier, like in [printf][57]
|
||||
* u: Unit size
|
||||
* b: `b`ytes
|
||||
h: `h`alf `w`ords (2 bytes)
|
||||
w: word (4 bytes)(default)
|
||||
* g: `g`iant word (8 bytes)
|
||||
|
||||
|
||||
|
||||
To print out the value at `rbp-0x4`, type `x/u $rbp-4`:
|
||||
To print out the value at `rbp-0x4`, type `x/u $rbp-4` :
|
||||
|
||||
![print value][58]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
If you keep this pattern in mind, it's straightforward to examine the memory. Check the [examining memory][59] section in the manual.
|
||||
|
||||
#### Manipulate the assembly
|
||||
|
||||
The arithmetic exception happened in the subroutine `zeroDivide()`. When you scroll a bit upward with the Up arrow key, you can find this pattern:
|
||||
|
||||
|
||||
```
|
||||
0x401211 <_Z10zeroDividev> push rbp
|
||||
0x401212 <_Z10zeroDividev+1> mov rbp,rsp
|
||||
0x401211 <_Z10zeroDividev> push rbp
|
||||
0x401212 <_Z10zeroDividev+1> mov rbp,rsp
|
||||
```
|
||||
|
||||
This is called the [function prologue][60]:
|
||||
|
||||
1. The base pointer (`rbp`) of the calling function is stored on the stack
|
||||
2. The value of the stack pointer (`rsp`) is loaded to the base pointer (`rbp`)
|
||||
1. The base pointer (rbp) of the calling function is stored on the stack
|
||||
2. The value of the stack pointer (rsp) is loaded to the base pointer (rbp)
|
||||
|
||||
|
||||
|
||||
Skip this subroutine completely. You can check the call stack with `backtrace`. You are only one stack frame ahead of your `main` function, so you can go back to `main` with a single `up`:
|
||||
Skip this subroutine completely. You can check the call stack with `backtrace`. You are only one stack frame ahead of your `main` function, so you can go back to `main` with a single `up` :
|
||||
|
||||
![Callstack assembly][61]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
In your `main` function, you can find this pattern:
|
||||
|
||||
|
||||
```
|
||||
0x401431 <main+497> cmp BYTE PTR [rbp-0x12],0x0
|
||||
0x401435 <main+501> je 0x40145f <main+543>
|
||||
0x401437 <main+503> call 0x401211<_Z10zeroDividev>
|
||||
0x401431 <main+497> cmp BYTE PTR [rbp-0x12],0x0
|
||||
0x401435 <main+501> je 0x40145f <main+543>
|
||||
0x401437 <main+503> call 0x401211<_Z10zeroDividev>
|
||||
```
|
||||
|
||||
The subroutine `zeroDivide()` is entered only when `jump equal (je)` evaluates to `true`. You can easily replace this with a `jump-not-equal (jne)` instruction, which has the opcode `0x75` (provided you are on an x86/64 architecture; the opcodes are different on other architectures). Restart the program by typing `run`. When the program stops at the entry function, manipulate the opcode by typing:
|
||||
|
||||
|
||||
```
|
||||
`set *(unsigned char*)0x401435 = 0x75`
|
||||
set *(unsigned char*)0x401435 = 0x75
|
||||
```
|
||||
|
||||
Finally, type `continue`. The program will skip the subroutine `zeroDivide()` and won't crash anymore.
|
||||
@ -573,8 +468,6 @@ You can find GDB working in the background in many integrated development enviro
|
||||
|
||||
![GDB in VSCodium][63]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][5])
|
||||
|
||||
It's useful to know how to leverage GDB's functionality. Usually, not all of GDB's functions can be used from the IDE, so you benefit from having experience using GDB from the command line.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -582,74 +475,74 @@ It's useful to know how to leverage GDB's functionality. Usually, not all of GDB
|
||||
via: https://opensource.com/article/21/1/gnu-project-debugger
|
||||
|
||||
作者:[Stephan Avenwedde][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][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/hansic99
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/mistake_bug_fix_find_error.png?itok=PZaz3dga (magnifying glass on computer screen, finding a bug in the code)
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/mistake_bug_fix_find_error.png
|
||||
[2]: https://www.gnu.org/software/gdb/
|
||||
[3]: https://opensource.com/article/20/8/linux-dump
|
||||
[4]: https://opensource.com/sites/default/files/uploads/gdb_output_no_dbg_symbols.png (gdb coredump output)
|
||||
[5]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[6]: tmp.2p0XrqmAS5#without_symbols
|
||||
[7]: https://opensource.com/sites/default/files/uploads/gdb_output_with_symbols.png (GDB output with symbols)
|
||||
[8]: https://opensource.com/sites/default/files/uploads/gdb_output_crash_on_c1_switch.png (gdb output crash on c1 switch)
|
||||
[9]: https://opensource.com/sites/default/files/uploads/gdb_output_info_source.png (gdb info source output)
|
||||
[10]: http://dwarfstd.org/
|
||||
[11]: https://sourceware.org/gdb/current/onlinedocs/gdb/Compilation.html#Compilation
|
||||
[12]: https://opensource.com/sites/default/files/uploads/gdb_output_info_shared.png (gdb info shared output)
|
||||
[13]: https://opensource.com/article/20/6/linux-libraries
|
||||
[14]: https://opensource.com/sites/default/files/uploads/gdb_output_stopped_by_sigint.png (gdb output stopped by sigint)
|
||||
[15]: https://opensource.com/sites/default/files/uploads/gdb_output_list_main.png (gdb output list main)
|
||||
[16]: https://opensource.com/sites/default/files/uploads/gdb_output_breakpoint_added.png (gdb output breakpoint added)
|
||||
[17]: https://opensource.com/sites/default/files/uploads/gdb_output_break_at_main.png (gdb output break at main)
|
||||
[18]: https://opensource.com/sites/default/files/images/gdb_output_screen_corrupted.png (gdb output corrupted)
|
||||
[19]: https://sourceware.org/gdb/onlinedocs/gdb/TUI-Keys.html#TUI-Keys
|
||||
[20]: https://opensource.com/sites/default/files/uploads/gdb_output_stop_on_watchpoint_1.png (gdb output stop on watchpoint_1)
|
||||
[21]: https://opensource.com/sites/default/files/uploads/gdb_output_stop_on_watchpoint_2.png (gdb output stop on watchpoint_2)
|
||||
[22]: https://opensource.com/sites/default/files/uploads/gdb_output_info_watchpoints.png (gdb output info watchpoints)
|
||||
[23]: https://opensource.com/sites/default/files/uploads/gdb_output_info_breakpoints.png (gdb output info breakpoints)
|
||||
[24]: https://opensource.com/sites/default/files/uploads/gdb_output_disabled_breakpoints.png (disabled breakpoints)
|
||||
[25]: https://opensource.com/sites/default/files/uploads/gdb_output_set_conditional_breakpoint.png (Set conditional breakpoint)
|
||||
[26]: https://opensource.com/sites/default/files/uploads/gdb_output_print_variable.png (print variable)
|
||||
[27]: https://opensource.com/sites/default/files/uploads/gdb_output_modify_breakpoint.png (modify breakpoint)
|
||||
[28]: https://opensource.com/sites/default/files/uploads/gdb_output_syscall_catch.png (catch syscall write output)
|
||||
[29]: https://sourceware.org/gdb/current/onlinedocs/gdb/Breakpoints.html#Breakpoints
|
||||
[30]: https://opensource.com/sites/default/files/uploads/gdb_output_print_and_modify.png (print variable)
|
||||
[31]: https://opensource.com/sites/default/files/uploads/gdb_output_whatis.png (whatis output)
|
||||
[32]: https://opensource.com/sites/default/files/uploads/gdb_output_info_scope_main.png (info scope main output)
|
||||
[33]: https://opensource.com/sites/default/files/uploads/gdb_output_info_locals_main.png (info locals output)
|
||||
[34]: https://sourceware.org/gdb/current/onlinedocs/gdb/Symbols.html
|
||||
[35]: https://man7.org/linux/man-pages/man1/ps.1.html
|
||||
[36]: https://man7.org/linux/man-pages/man1/top.1.html
|
||||
[37]: https://opensource.com/sites/default/files/uploads/coredump_running.png (coredump application)
|
||||
[38]: https://opensource.com/sites/default/files/uploads/gdb_output_attaching_to_process.png (attach GDB to coredump)
|
||||
[39]: https://opensource.com/sites/default/files/uploads/gdb_output_backtrace.png (layout src and backtrace output)
|
||||
[40]: https://sourceware.org/gdb/current/onlinedocs/gdb/Attach.html#Attach
|
||||
[41]: https://opensource.com/sites/default/files/uploads/gdb_output_stackframe_up.png (moving up the stack to main.cpp)
|
||||
[42]: https://sourceware.org/gdb/current/onlinedocs/gdb/Stack.html#Stack
|
||||
[43]: https://ftp.gnu.org/old-gnu/Manuals/gdb/html_node/gdb_48.html#SEC49
|
||||
[44]: https://opensource.com/sites/default/files/uploads/crash.png (Crash meme)
|
||||
[45]: https://opensource.com/sites/default/files/uploads/gdb_output_coredump.png (coredump output)
|
||||
[46]: https://opensource.com/sites/default/files/uploads/gdb_output_up_five.png (up 5 output)
|
||||
[4]: https://opensource.com/sites/default/files/uploads/gdb_output_no_dbg_symbols.png
|
||||
[5]: https://opensource.com/sites/default/files/uploads/gdb_output_with_symbols.png
|
||||
[6]: https://opensource.com/sites/default/files/uploads/gdb_output_crash_on_c1_switch.png
|
||||
[7]: https://opensource.com/sites/default/files/uploads/gdb_output_info_source.png
|
||||
[8]: http://dwarfstd.org/
|
||||
[9]: https://sourceware.org/gdb/current/onlinedocs/gdb/Compilation.html#Compilation
|
||||
[10]: https://opensource.com/sites/default/files/uploads/gdb_output_info_shared.png
|
||||
[11]: https://opensource.com/article/20/6/linux-libraries
|
||||
[12]: https://opensource.com/sites/default/files/uploads/gdb_output_stopped_by_sigint.png
|
||||
[13]: https://opensource.com/sites/default/files/uploads/gdb_output_list_main.png
|
||||
[14]: https://opensource.com/sites/default/files/uploads/gdb_output_breakpoint_added.png
|
||||
[15]: https://opensource.com/sites/default/files/uploads/gdb_output_break_at_main.png
|
||||
[16]: https://opensource.com/sites/default/files/images/gdb_output_screen_corrupted.png
|
||||
[17]: https://sourceware.org/gdb/onlinedocs/gdb/TUI-Keys.html#TUI-Keys
|
||||
[18]: https://opensource.com/sites/default/files/uploads/gdb_output_stop_on_watchpoint_1.png
|
||||
[19]: https://opensource.com/sites/default/files/uploads/gdb_output_stop_on_watchpoint_2.png
|
||||
[20]: https://opensource.com/sites/default/files/uploads/gdb_output_info_watchpoints.png
|
||||
[21]: https://opensource.com/sites/default/files/uploads/gdb_output_info_breakpoints.png
|
||||
[22]: https://opensource.com/sites/default/files/uploads/gdb_output_disabled_breakpoints.png
|
||||
[23]: https://opensource.com/sites/default/files/uploads/gdb_output_set_conditional_breakpoint.png
|
||||
[24]: https://opensource.com/sites/default/files/uploads/gdb_output_print_variable.png
|
||||
[25]: https://opensource.com/sites/default/files/uploads/gdb_output_modify_breakpoint.png
|
||||
[26]: https://opensource.com/sites/default/files/uploads/gdb_output_syscall_catch.png
|
||||
[27]: https://sourceware.org/gdb/current/onlinedocs/gdb/Breakpoints.html#Breakpoints
|
||||
[28]: https://opensource.com/sites/default/files/uploads/gdb_output_print_and_modify.png
|
||||
[29]: https://opensource.com/sites/default/files/uploads/gdb_output_whatis.png
|
||||
[30]: https://opensource.com/sites/default/files/uploads/gdb_output_info_scope_main.png
|
||||
[31]: https://opensource.com/sites/default/files/uploads/gdb_output_info_locals_main.png
|
||||
[32]: https://sourceware.org/gdb/current/onlinedocs/gdb/Symbols.html
|
||||
[33]: https://man7.org/linux/man-pages/man1/ps.1.html
|
||||
[34]: https://man7.org/linux/man-pages/man1/top.1.html
|
||||
[35]: https://opensource.com/sites/default/files/uploads/coredump_running.png
|
||||
[36]: https://opensource.com/sites/default/files/uploads/gdb_output_attaching_to_process.png
|
||||
[37]: https://opensource.com/sites/default/files/uploads/gdb_output_backtrace.png
|
||||
[38]: https://sourceware.org/gdb/current/onlinedocs/gdb/Attach.html#Attach
|
||||
[39]: https://opensource.com/sites/default/files/uploads/gdb_output_stackframe_up.png
|
||||
[40]: https://sourceware.org/gdb/current/onlinedocs/gdb/Stack.html#Stack
|
||||
[41]: https://ftp.gnu.org/old-gnu/Manuals/gdb/html_node/gdb_48.html#SEC49
|
||||
[42]: https://opensource.com/article/20/8/linux-dump
|
||||
[43]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[44]: https://opensource.com/sites/default/files/uploads/crash.png
|
||||
[45]: https://opensource.com/sites/default/files/uploads/gdb_output_coredump.png
|
||||
[46]: https://opensource.com/sites/default/files/uploads/gdb_output_up_five.png
|
||||
[47]: https://en.wikipedia.org/wiki/Assembly_language
|
||||
[48]: https://opensource.com/sites/default/files/uploads/gdb_output_no_debugging_symbols.png (no debugging symbols)
|
||||
[49]: https://opensource.com/sites/default/files/uploads/gdb_output_info_file.png (info file output)
|
||||
[50]: https://opensource.com/sites/default/files/uploads/gdb_output_break_at_start.png (breakpoint at the entry point)
|
||||
[48]: https://opensource.com/sites/default/files/uploads/gdb_output_no_debugging_symbols.png
|
||||
[49]: https://opensource.com/sites/default/files/uploads/gdb_output_info_file.png
|
||||
[50]: https://opensource.com/sites/default/files/uploads/gdb_output_break_at_start.png
|
||||
[51]: https://en.wikipedia.org/wiki/X86_assembly_language#Syntax
|
||||
[52]: https://opensource.com/sites/default/files/uploads/gdb_output_disassembly_flavor.png (changing assembly flavor)
|
||||
[53]: https://opensource.com/sites/default/files/uploads/gdb_output_layout_reg_asm.png (layout asm and layout reg output)
|
||||
[52]: https://opensource.com/sites/default/files/uploads/gdb_output_disassembly_flavor.png
|
||||
[53]: https://opensource.com/sites/default/files/uploads/gdb_output_layout_reg_asm.png
|
||||
[54]: https://github.com/hANSIc99/core_dump_example/blob/master/gdbinit
|
||||
[55]: https://opensource.com/sites/default/files/uploads/gdb_output_asm_div_zero.png (continuing execution after crash)
|
||||
[56]: https://opensource.com/sites/default/files/uploads/gdb_output_stack_division.png (stack division output)
|
||||
[55]: https://opensource.com/sites/default/files/uploads/gdb_output_asm_div_zero.png
|
||||
[56]: https://opensource.com/sites/default/files/uploads/gdb_output_stack_division.png
|
||||
[57]: https://en.wikipedia.org/wiki/Printf_format_string#Type_field
|
||||
[58]: https://opensource.com/sites/default/files/uploads/gdb_output_examine_1.png (print value)
|
||||
[58]: https://opensource.com/sites/default/files/uploads/gdb_output_examine_1.png
|
||||
[59]: https://sourceware.org/gdb/current/onlinedocs/gdb/Memory.html
|
||||
[60]: https://en.wikipedia.org/wiki/Function_prologue
|
||||
[61]: https://opensource.com/sites/default/files/uploads/gdb_output_callstack_assembly_0.png (Callstack assembly)
|
||||
[61]: https://opensource.com/sites/default/files/uploads/gdb_output_callstack_assembly_0.png
|
||||
[62]: https://github.com/WebFreak001/code-debug
|
||||
[63]: https://opensource.com/sites/default/files/uploads/vs_codium_native_debug.png (GDB in VSCodium)
|
||||
[63]: https://opensource.com/sites/default/files/uploads/vs_codium_native_debug.png
|
||||
|
@ -1,18 +1,20 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Analyze Kubernetes files for errors with KubeLinter)
|
||||
[#]: via: (https://opensource.com/article/21/1/kubelinter)
|
||||
[#]: author: (Jessica Cherry https://opensource.com/users/cherrybomb)
|
||||
[#]: subject: "Analyze Kubernetes files for errors with KubeLinter"
|
||||
[#]: via: "https://opensource.com/article/21/1/kubelinter"
|
||||
[#]: author: "Jessica Cherry https://opensource.com/users/cherrybomb"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Analyze Kubernetes files for errors with KubeLinter
|
||||
======
|
||||
Find and fix errors in your Helm charts and Kubernetes configuration
|
||||
files with KubeLinter.
|
||||
Find and fix errors in your Helm charts and Kubernetes configuration files with KubeLinter.
|
||||
|
||||
![magnifying glass on computer screen, finding a bug in the code][1]
|
||||
|
||||
Image by: Opensource.com
|
||||
|
||||
[KubeLinter][2] is an open source project released by Stackrox to analyze Kubernetes YAML files for security issues and errant code. The tool covers Helm charts and Kubernetes configuration files, including [Knative][3] files. Using it can improve cloud-native development, reduce development time, and encourage DevOps best practices.
|
||||
|
||||
### Download and install
|
||||
@ -23,23 +25,20 @@ You have several options to install KubeLinter.
|
||||
|
||||
You can install manually from the Git repository:
|
||||
|
||||
|
||||
```
|
||||
$ git clone [git@github.com][4]:stackrox/kube-linter.git
|
||||
$ cd kube-linter && make build
|
||||
$ git clone git@github.com:stackrox/kube-linter.git
|
||||
$ cd kube-linter && make build
|
||||
$ .gobin/kube-linter version
|
||||
```
|
||||
|
||||
If you use [Homebrew][5], you can install it with the `brew` command:
|
||||
|
||||
If you use [Homebrew][4], you can install it with the `brew` command:
|
||||
|
||||
```
|
||||
`$ brew install kube-linter`
|
||||
$ brew install kube-linter
|
||||
```
|
||||
|
||||
You can also install it with Go (as I did):
|
||||
|
||||
|
||||
```
|
||||
$ GO111MODULE=on go get golang.stackrox.io/kube-linter/cmd/kube-linter
|
||||
go: finding golang.stackrox.io/kube-linter latest
|
||||
@ -48,11 +47,10 @@ go: extracting golang.stackrox.io/kube-linter v0.0.0-20201204022312-475075c74675
|
||||
[...]
|
||||
```
|
||||
|
||||
After installing, you must make an alias in your `~/.bashrc`:
|
||||
|
||||
After installing, you must make an alias in your `~/.bashrc` :
|
||||
|
||||
```
|
||||
$ echo "alias kube-linter=$HOME/go/bin/kube-linter" >> ~/.bashrc
|
||||
$ echo "alias kube-linter=$HOME/go/bin/kube-linter" >> ~/.bashrc
|
||||
$ source ~/.bashrc
|
||||
```
|
||||
|
||||
@ -60,7 +58,6 @@ $ source ~/.bashrc
|
||||
|
||||
Now that the tool is installed, try it out on a Helm chart. First, start Minikube with a clean build and some small configuration changes:
|
||||
|
||||
|
||||
```
|
||||
$ minikube config set kubernetes-version v1.19.0
|
||||
$ minikube config set memory 8000
|
||||
@ -68,23 +65,22 @@ $ minikube config set memory 8000
|
||||
$ minikube config set cpus 12
|
||||
❗ These changes will take effect upon a minikube delete and then a minikube start
|
||||
$ minikube delete
|
||||
🔥 Deleting "minikube" in docker ...
|
||||
🔥 Deleting container "minikube" ...
|
||||
🔥 Removing /home/jess/.minikube/machines/minikube ...
|
||||
💀 Removed all traces of the "minikube" cluster.
|
||||
? Deleting "minikube" in docker ...
|
||||
? Deleting container "minikube" ...
|
||||
? Removing /home/jess/.minikube/machines/minikube ...
|
||||
? Removed all traces of the "minikube" cluster.
|
||||
|
||||
$ minikube start
|
||||
😄 minikube v1.14.2 on Debian bullseye/sid
|
||||
? minikube v1.14.2 on Debian bullseye/sid
|
||||
✨ Using the docker driver based on user configuration
|
||||
👍 Starting control plane node minikube in cluster minikube
|
||||
🎉 minikube 1.15.1 is available! Download it: <https://github.com/kubernetes/minikube/releases/tag/v1.15.1>
|
||||
💡 To disable this notice, run: 'minikube config set WantUpdateNotification false'
|
||||
? Starting control plane node minikube in cluster minikube
|
||||
? minikube 1.15.1 is available! Download it: https://github.com/kubernetes/minikube/releases/tag/v1.15.1
|
||||
? To disable this notice, run: 'minikube config set WantUpdateNotification false'
|
||||
|
||||
💾 Downloading Kubernetes v1.19.0 preload ...
|
||||
? Downloading Kubernetes v1.19.0 preload ...
|
||||
```
|
||||
|
||||
Once everything is running, create an example Helm chart called `first_test`:
|
||||
|
||||
Once everything is running, create an example Helm chart called `first_test` :
|
||||
|
||||
```
|
||||
$ helm create first_test
|
||||
@ -95,7 +91,6 @@ first_test
|
||||
|
||||
Test KubeLinter against the new, unedited chart. Run the `kube-linter` command to see the available commands and flags:
|
||||
|
||||
|
||||
```
|
||||
$ kube-linter
|
||||
Usage:
|
||||
@ -103,8 +98,8 @@ Usage:
|
||||
|
||||
Available Commands:
|
||||
checks View more information on lint checks
|
||||
help Help about any command
|
||||
lint Lint Kubernetes YAML files and Helm charts
|
||||
help Help about any command
|
||||
lint Lint Kubernetes YAML files and Helm charts
|
||||
templates View more information on check templates
|
||||
version Print version and exit
|
||||
|
||||
@ -116,13 +111,12 @@ Use "/home/jess/go/bin/kube-linter [command] --help" for more information about
|
||||
|
||||
Then test what the basic `lint` command does to your example chart. You'll end up with many errors, so I'll grab a snippet of some issues:
|
||||
|
||||
|
||||
```
|
||||
$ kube-linter lint first_test/
|
||||
|
||||
first_test/first_test/templates/deployment.yaml: (object: <no namespace>/test-release-first_test apps/v1, Kind=Deployment) container "first_test" does not have a read-only root file system (check: no-read-only-root-fs, remediation: Set readOnlyRootFilesystem to true in your container's securityContext.)
|
||||
first_test/first_test/templates/deployment.yaml: (object: <no namespace>/test-release-first_test apps/v1, Kind=Deployment) container "first_test" does not have a read-only root file system (check: no-read-only-root-fs, remediation: Set readOnlyRootFilesystem to true in your container's securityContext.)
|
||||
|
||||
first_test/first_test/templates/deployment.yaml: (object: <no namespace>/test-release-first_test apps/v1, Kind=Deployment) container "first_test" is not set to runAsNonRoot (check: run-as-non-root, remediation: Set runAsUser to a non-zero number, and runAsNonRoot to true, in your pod or container securityContext. See <https://kubernetes.io/docs/tasks/configure-pod-container/security-context/> for more details.)
|
||||
first_test/first_test/templates/deployment.yaml: (object: <no namespace>/test-release-first_test apps/v1, Kind=Deployment) container "first_test" is not set to runAsNonRoot (check: run-as-non-root, remediation: Set runAsUser to a non-zero number, and runAsNonRoot to true, in your pod or container securityContext. See https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ for more details.)
|
||||
[...]
|
||||
Error: found 12 lint errors
|
||||
```
|
||||
@ -131,14 +125,12 @@ For the sake of brevity, I picked two security issues that are easy for me to f
|
||||
|
||||
The `kube-linter` output provides hints about the required fixes. For instance, the first error ends with:
|
||||
|
||||
|
||||
```
|
||||
`remediation: Set readOnlyRootFilesystem to true in your container's securityContext.`
|
||||
remediation: Set readOnlyRootFilesystem to true in your container's securityContext.
|
||||
```
|
||||
|
||||
The next step is clear: Open the `values.yaml` file in a text editor (I use Vi, but you can use whatever you like) and locate the `securityContext` section:
|
||||
|
||||
|
||||
```
|
||||
securityContext: {}
|
||||
# capabilities:
|
||||
@ -151,12 +143,11 @@ securityContext: {}
|
||||
|
||||
Uncomment the section and remove the braces:
|
||||
|
||||
|
||||
```
|
||||
securityContext:
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
- ALL
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
@ -164,20 +155,18 @@ securityContext:
|
||||
|
||||
Save the file and rerun the linter. Those errors no longer show up in the list, and the error count changes.
|
||||
|
||||
|
||||
```
|
||||
`Error: found 10 lint errors`
|
||||
Error: found 10 lint errors
|
||||
```
|
||||
|
||||
Congratulations! You have resolved security issues!
|
||||
|
||||
### Kubernetes with KubeLinter
|
||||
|
||||
This example uses an app file from my [previous article on Knative][6] to test against Kubernetes config files. I already have Knative up and running, so you may want to review that article if it's not running on your system.
|
||||
This example uses an app file from my [previous article on Knative][5] to test against Kubernetes config files. I already have Knative up and running, so you may want to review that article if it's not running on your system.
|
||||
|
||||
I downloaded the Kourier service YAML file for this example:
|
||||
|
||||
|
||||
```
|
||||
$ ls
|
||||
kourier.yaml first_test
|
||||
@ -185,13 +174,12 @@ kourier.yaml first_test
|
||||
|
||||
Start by running the linter against `kourier.yaml`. Again, there are several issues. I'll focus on resource problems:
|
||||
|
||||
|
||||
```
|
||||
$ kube-linter lint kourier.yaml
|
||||
$ kube-linter lint kourier.yaml
|
||||
|
||||
kourier.yaml: (object: kourier-system/3scale-kourier-gateway apps/v1, Kind=Deployment) container "kourier-gateway" has cpu limit 0 (check: unset-cpu-requirements, remediation: Set your container's CPU requests and limits depending on its requirements. See <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/\#requests-and-limits> for more details.)
|
||||
kourier.yaml: (object: kourier-system/3scale-kourier-gateway apps/v1, Kind=Deployment) container "kourier-gateway" has cpu limit 0 (check: unset-cpu-requirements, remediation: Set your container's CPU requests and limits depending on its requirements. See https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits for more details.)
|
||||
|
||||
kourier.yaml: (object: kourier-system/3scale-kourier-gateway apps/v1, Kind=Deployment) container "kourier-gateway" has memory request 0 (check: unset-memory-requirements, remediation: Set your container's memory requests and limits depending on its requirements. See <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/\#requests-and-limits> for more details.)
|
||||
kourier.yaml: (object: kourier-system/3scale-kourier-gateway apps/v1, Kind=Deployment) container "kourier-gateway" has memory request 0 (check: unset-memory-requirements, remediation: Set your container's memory requests and limits depending on its requirements. See https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits for more details.)
|
||||
|
||||
Error: found 12 lint errors
|
||||
```
|
||||
@ -200,7 +188,6 @@ Since this is a single deployment file, you can edit it directly. Open it in a t
|
||||
|
||||
Start with deployment:
|
||||
|
||||
|
||||
```
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@ -214,70 +201,67 @@ metadata:
|
||||
|
||||
The containers section has some problems:
|
||||
|
||||
|
||||
```
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --base-id 1
|
||||
- -c /tmp/config/envoy-bootstrap.yaml
|
||||
- --log-level info
|
||||
command:
|
||||
- /usr/local/bin/envoy
|
||||
image: docker.io/maistra/proxyv2-ubi8:1.1.5
|
||||
imagePullPolicy: Always
|
||||
name: kourier-gateway
|
||||
ports:
|
||||
- name: http2-external
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
- name: http2-internal
|
||||
containerPort: 8081
|
||||
protocol: TCP
|
||||
- name: https-external
|
||||
containerPort: 8443
|
||||
protocol: TCP
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --base-id 1
|
||||
- -c /tmp/config/envoy-bootstrap.yaml
|
||||
- --log-level info
|
||||
command:
|
||||
- /usr/local/bin/envoy
|
||||
image: docker.io/maistra/proxyv2-ubi8:1.1.5
|
||||
imagePullPolicy: Always
|
||||
name: kourier-gateway
|
||||
ports:
|
||||
- name: http2-external
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
- name: http2-internal
|
||||
containerPort: 8081
|
||||
protocol: TCP
|
||||
- name: https-external
|
||||
containerPort: 8443
|
||||
protocol: TCP
|
||||
```
|
||||
|
||||
Add some specs to the container configuration:
|
||||
|
||||
|
||||
```
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --base-id 1
|
||||
- -c /tmp/config/envoy-bootstrap.yaml
|
||||
- --log-level info
|
||||
command:
|
||||
- /usr/local/bin/envoy
|
||||
image: docker.io/maistra/proxyv2-ubi8:1.1.5
|
||||
imagePullPolicy: Always
|
||||
name: kourier-gateway
|
||||
ports:
|
||||
- name: http2-external
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
- name: http2-internal
|
||||
containerPort: 8081
|
||||
protocol: TCP
|
||||
- name: https-external
|
||||
containerPort: 8443
|
||||
protocol: TCP
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --base-id 1
|
||||
- -c /tmp/config/envoy-bootstrap.yaml
|
||||
- --log-level info
|
||||
command:
|
||||
- /usr/local/bin/envoy
|
||||
image: docker.io/maistra/proxyv2-ubi8:1.1.5
|
||||
imagePullPolicy: Always
|
||||
name: kourier-gateway
|
||||
ports:
|
||||
- name: http2-external
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
- name: http2-internal
|
||||
containerPort: 8081
|
||||
protocol: TCP
|
||||
- name: https-external
|
||||
containerPort: 8443
|
||||
protocol: TCP
|
||||
resources:
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
```
|
||||
|
||||
When you rerun the linter, you'll notice these issues no longer show in the output, and the error count changes:
|
||||
|
||||
|
||||
```
|
||||
`Error: found 8 lint errors`
|
||||
Error: found 8 lint errors
|
||||
```
|
||||
|
||||
Congratulations! You have fixed resource issues in your Kubernetes file!
|
||||
@ -293,17 +277,16 @@ I think KubeLinter's best part is that each error message includes documentation
|
||||
via: https://opensource.com/article/21/1/kubelinter
|
||||
|
||||
作者:[Jessica Cherry][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][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/cherrybomb
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/mistake_bug_fix_find_error.png?itok=PZaz3dga (magnifying glass on computer screen, finding a bug in the code)
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/mistake_bug_fix_find_error.png
|
||||
[2]: https://github.com/stackrox/kube-linter
|
||||
[3]: https://knative.dev/
|
||||
[4]: mailto:git@github.com
|
||||
[5]: https://opensource.com/article/20/6/homebrew-linux
|
||||
[6]: https://opensource.com/article/20/11/knative
|
||||
[4]: https://opensource.com/article/20/6/homebrew-linux
|
||||
[5]: https://opensource.com/article/20/11/knative
|
||||
|
@ -1,40 +1,40 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How I programmed a virtual gift exchange)
|
||||
[#]: via: (https://opensource.com/article/21/1/open-source-gift-exchange)
|
||||
[#]: author: (Chris Hermansen https://opensource.com/users/clhermansen)
|
||||
[#]: subject: "How I programmed a virtual gift exchange"
|
||||
[#]: via: "https://opensource.com/article/21/1/open-source-gift-exchange"
|
||||
[#]: author: "Chris Hermansen https://opensource.com/users/clhermansen"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
How I programmed a virtual gift exchange
|
||||
======
|
||||
A book club takes its annual gift exchange online with the help of HTML,
|
||||
CSS, and JavaScript.
|
||||
A book club takes its annual gift exchange online with the help of HTML, CSS, and JavaScript.
|
||||
|
||||
![Package wrapped with brown paper and red bow][1]
|
||||
|
||||
Image by: Photo by [Jess Bailey][2] on [Unsplash][3]
|
||||
|
||||
Every year, my wife's book club has a book exchange during the holidays. Due to the need to maintain physical distance in 2020, I created an online gift exchange for them to use during a book club videoconference. Apparently, the virtual book exchange worked out (at least, I received kind compliments from the book club members), so I decided to share this simple little hack.
|
||||
|
||||
### How the book exchange usually works
|
||||
|
||||
In past years, the exchange has gone something like this:
|
||||
|
||||
1. Each person buys a book and wraps it up.
|
||||
2. Everyone arrives at the host's home and puts the wrapped books in a pile.
|
||||
3. Each person draws a number out of a hat, which establishes their turn.
|
||||
4. The person who drew No. 1 selects a book from the pile and unwraps it. In turn, each subsequent person chooses to either take a wrapped book from the pile or to steal an unwrapped book from someone who has gone before.
|
||||
5. When someone's book is stolen, they can either replace it with a wrapped book from the pile or steal another book (but not the one that was stolen from them) from someone else.
|
||||
6. And so on… eventually, someone has to take the last unwrapped book to end the exchange.
|
||||
|
||||
|
||||
1. Each person buys a book and wraps it up.
|
||||
2. Everyone arrives at the host's home and puts the wrapped books in a pile.
|
||||
3. Each person draws a number out of a hat, which establishes their turn.
|
||||
4. The person who drew No. 1 selects a book from the pile and unwraps it. In turn, each subsequent person chooses to either take a wrapped book from the pile or to steal an unwrapped book from someone who has gone before.
|
||||
5. When someone's book is stolen, they can either replace it with a wrapped book from the pile or steal another book (but not the one that was stolen from them) from someone else.
|
||||
6. And so on… eventually, someone has to take the last unwrapped book to end the exchange.
|
||||
|
||||
### Designing the virtual book exchange
|
||||
|
||||
My first decision was which implementation platform to use for the book exchange. Because there would already be a browser open to host the videoconference, I decided to use HTML, CSS, and JavaScript.
|
||||
|
||||
Then it was design time. After some thinking, I decided to use rectangles to represent the book club members and the books. The books would be draggable, and when one was dropped on a member's rectangle, the book would unwrap (and stay unwrapped). I needed some "wrapping paper," so I used this source of [free-to-use images][2].
|
||||
Then it was design time. After some thinking, I decided to use rectangles to represent the book club members and the books. The books would be draggable, and when one was dropped on a member's rectangle, the book would unwrap (and stay unwrapped). I needed some "wrapping paper," so I used this source of [free-to-use images][4].
|
||||
|
||||
I took screenshots of the patterns I liked and used [GIMP][3] to scale the images to the right width and height.
|
||||
I took screenshots of the patterns I liked and used [GIMP][5] to scale the images to the right width and height.
|
||||
|
||||
I needed a way to handle draggable and droppable interactions; given that I've been using jQuery and jQuery UI for several years now, I decided to continue along that path.
|
||||
|
||||
@ -42,25 +42,19 @@ For a while, I struggled with what a droppable element should do when something
|
||||
|
||||
Jumping to the results, here's a screenshot of the user interface at the beginning of the exchange:
|
||||
|
||||
![Virtual book exchange][4]
|
||||
|
||||
(Chris Hermansen, [CC BY-SA 4.0][5])
|
||||
![Virtual book exchange][6]
|
||||
|
||||
There are nine book club members: Wanda, Carlos, Bill, and so on. There are also nine fairly ugly wrapped parcels.
|
||||
|
||||
Let's say Wanda goes first and chooses the flower wrapping paper. The host clicks and drags that parcel to Wanda's name, and the parcel unwraps:
|
||||
|
||||
![Virtual book exchange][6]
|
||||
|
||||
(Chris Hermansen, [CC BY-SA 4.0][5])
|
||||
![Virtual book exchange][7]
|
||||
|
||||
Whoops! That title and author are a bit too long to fit on the book's "cover." Oh well, I'll fix that in the next version.
|
||||
|
||||
Carlos is next. He decides he really wants to read that book, so he steals it. Wanda then chooses the paisley pattern, and the screen looks like this:
|
||||
|
||||
![Virtual book exchange][7]
|
||||
|
||||
(Chris Hermansen, [CC BY-SA 4.0][5])
|
||||
![Virtual book exchange][8]
|
||||
|
||||
And so on until the exchange ends.
|
||||
|
||||
@ -68,120 +62,117 @@ And so on until the exchange ends.
|
||||
|
||||
So what about the code? Here it is:
|
||||
|
||||
|
||||
```
|
||||
1 <!doctype html>
|
||||
2 <[html][8] lang="en">
|
||||
3 <[head][9]>
|
||||
4 <[meta][10] charset="utf-8">
|
||||
5 <[title][11]>Book Exchange</[title][11]>
|
||||
6 <[link][12] rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css">
|
||||
7 <[style][13]>
|
||||
8 .draggable {
|
||||
9 float: left;
|
||||
10 width: 90px;
|
||||
11 height: 90px;
|
||||
12 background: #ccc;
|
||||
13 padding: 5px;
|
||||
14 margin: 5px 5px 5px 0;
|
||||
15 }
|
||||
16 .droppable {
|
||||
17 float: left;
|
||||
18 width: 100px;
|
||||
19 height: 125px;
|
||||
20 background: #999;
|
||||
21 color: #fff;
|
||||
22 padding: 10px;
|
||||
23 margin: 10px 10px 10px 0;
|
||||
24 }
|
||||
25 </[style][13]>
|
||||
26 <[script][14] src="[https://code.jquery.com/jquery-1.12.4.js"\>\][15]</[script][14]>
|
||||
27 <[script][14] src="[https://code.jquery.com/ui/1.12.1/jquery-ui.js"\>\][16]</[script][14]>
|
||||
28 </[head][9]>
|
||||
29 <[body][17]>
|
||||
30 <[h1][18] style="color:#1a1aff;">Raffles Book Club Remote Gift Exchange</[h1][18]>
|
||||
31 <[h2][19] style="color:#aa0a0a;">The players, in random order, and the luxurious gifts, wrapped:</[h2][19]>
|
||||
32
|
||||
33 <[div][20]>
|
||||
34 <[div][20] id="wanda" class="droppable">Wanda</[div][20]>
|
||||
35 <[div][20] id="carlos" class="droppable">Carlos</[div][20]>
|
||||
36 <[div][20] id="bill" class="droppable">Bill</[div][20]>
|
||||
37 <[div][20] id="arlette" class="droppable">Arlette</[div][20]>
|
||||
38 <[div][20] id="joanne" class="droppable">Joanne</[div][20]>
|
||||
39 <[div][20] id="aleks" class="droppable">Alekx</[div][20]>
|
||||
40 <[div][20] id="ermintrude" class="droppable">Ermintrude</[div][20]>
|
||||
41 <[div][20] id="walter" class="droppable">Walter</[div][20]>
|
||||
42 <[div][20] id="hilary" class="droppable">Hilary</[div][20]>
|
||||
43 </[div][20]>
|
||||
44 <[div][20]>
|
||||
45 <[div][20] id="bows" class="draggable" style="background-image: url('bows.png');"></[div][20]>
|
||||
46 <[div][20] id="boxes" class="draggable" style="background-image: url('boxes.png');"></[div][20]>
|
||||
47 <[div][20] id="circles" class="draggable" style="background-image: url('circles.png');"></[div][20]>
|
||||
48 <[div][20] id="gerbers" class="draggable" style="background-image: url('gerbers.png');"></[div][20]>
|
||||
49 <[div][20] id="hippie" class="draggable" style="background-image: url('hippie.png');"></[div][20]>
|
||||
50 <[div][20] id="lattice" class="draggable" style="background-image: url('lattice.png');"></[div][20]>
|
||||
51 <[div][20] id="nautical" class="draggable" style="background-image: url('nautical.png');"></[div][20]>
|
||||
52 <[div][20] id="splodges" class="draggable" style="background-image: url('splodges.png');"></[div][20]>
|
||||
53 <[div][20] id="ugly" class="draggable" style="background-image: url('ugly.png');"></[div][20]>
|
||||
54 </[div][20]>
|
||||
55
|
||||
56 <[script][14]>
|
||||
57 var books = {
|
||||
58 'bows': 'Untamed by Glennon Doyle',
|
||||
59 'boxes': "The Heart's Invisible Furies by John Boyne",
|
||||
60 'circles': 'The Great Halifax Explosion by John Bacon',
|
||||
61 'gerbers': 'Homes: A Refugee Story by Abu Bakr al Rabeeah, Winnie Yeung',
|
||||
62 'hippie': 'Before We Were Yours by Lisa Wingate',
|
||||
63 'lattice': "Hamnet and Judith by Maggie O'Farrell",
|
||||
64 'nautical': 'Shuggy Bain by Douglas Stewart',
|
||||
65 'splodges': 'Magdalena by Wade Davis',
|
||||
66 'ugly': 'Funny Boy by Shyam Selvadurai'
|
||||
67 };
|
||||
68 $( ".droppable" ).droppable({
|
||||
69 drop: function(event, ui) {
|
||||
70 var element = $(ui.draggable[0]);
|
||||
71 var wrapping = element.attr('id');
|
||||
72 /* alert( $(this).text() + " got " + wrapping); */
|
||||
73 $(ui.draggable[0]).css("background-image","url(book_cover.png)");
|
||||
74 $(ui.draggable[0]).text(books[wrapping]);
|
||||
75 },
|
||||
76 out: function() {
|
||||
77 /* alert( $(this).text() + " lost it" ); */
|
||||
78 }
|
||||
79 });
|
||||
80 $( ".draggable" ).draggable();
|
||||
81 </[script][14]>
|
||||
82
|
||||
83 </[body][17]>
|
||||
84 </[html][8]>
|
||||
1 <!doctype html>
|
||||
2 <html lang="en">
|
||||
3 <head>
|
||||
4 <meta charset="utf-8">
|
||||
5 <title>Book Exchange</title>
|
||||
6 <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css">
|
||||
7 <style>
|
||||
8 .draggable {
|
||||
9 float: left;
|
||||
10 width: 90px;
|
||||
11 height: 90px;
|
||||
12 background: #ccc;
|
||||
13 padding: 5px;
|
||||
14 margin: 5px 5px 5px 0;
|
||||
15 }
|
||||
16 .droppable {
|
||||
17 float: left;
|
||||
18 width: 100px;
|
||||
19 height: 125px;
|
||||
20 background: #999;
|
||||
21 color: #fff;
|
||||
22 padding: 10px;
|
||||
23 margin: 10px 10px 10px 0;
|
||||
24 }
|
||||
25 </style>
|
||||
26 <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
|
||||
27 <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
|
||||
28 </head>
|
||||
29 <body>
|
||||
30 <h1 style="color:#1a1aff;">Raffles Book Club Remote Gift Exchange</h1>
|
||||
31 <h2 style="color:#aa0a0a;">The players, in random order, and the luxurious gifts, wrapped:</h2>
|
||||
32
|
||||
33 <div>
|
||||
34 <div id="wanda" class="droppable">Wanda</div>
|
||||
35 <div id="carlos" class="droppable">Carlos</div>
|
||||
36 <div id="bill" class="droppable">Bill</div>
|
||||
37 <div id="arlette" class="droppable">Arlette</div>
|
||||
38 <div id="joanne" class="droppable">Joanne</div>
|
||||
39 <div id="aleks" class="droppable">Alekx</div>
|
||||
40 <div id="ermintrude" class="droppable">Ermintrude</div>
|
||||
41 <div id="walter" class="droppable">Walter</div>
|
||||
42 <div id="hilary" class="droppable">Hilary</div>
|
||||
43 </div>
|
||||
44 <div>
|
||||
45 <div id="bows" class="draggable" style="background-image: url('bows.png');"></div>
|
||||
46 <div id="boxes" class="draggable" style="background-image: url('boxes.png');"></div>
|
||||
47 <div id="circles" class="draggable" style="background-image: url('circles.png');"></div>
|
||||
48 <div id="gerbers" class="draggable" style="background-image: url('gerbers.png');"></div>
|
||||
49 <div id="hippie" class="draggable" style="background-image: url('hippie.png');"></div>
|
||||
50 <div id="lattice" class="draggable" style="background-image: url('lattice.png');"></div>
|
||||
51 <div id="nautical" class="draggable" style="background-image: url('nautical.png');"></div>
|
||||
52 <div id="splodges" class="draggable" style="background-image: url('splodges.png');"></div>
|
||||
53 <div id="ugly" class="draggable" style="background-image: url('ugly.png');"></div>
|
||||
54 </div>
|
||||
55
|
||||
56 <script>
|
||||
57 var books = {
|
||||
58 'bows': 'Untamed by Glennon Doyle',
|
||||
59 'boxes': "The Heart's Invisible Furies by John Boyne",
|
||||
60 'circles': 'The Great Halifax Explosion by John Bacon',
|
||||
61 'gerbers': 'Homes: A Refugee Story by Abu Bakr al Rabeeah, Winnie Yeung',
|
||||
62 'hippie': 'Before We Were Yours by Lisa Wingate',
|
||||
63 'lattice': "Hamnet and Judith by Maggie O'Farrell",
|
||||
64 'nautical': 'Shuggy Bain by Douglas Stewart',
|
||||
65 'splodges': 'Magdalena by Wade Davis',
|
||||
66 'ugly': 'Funny Boy by Shyam Selvadurai'
|
||||
67 };
|
||||
68 $( ".droppable" ).droppable({
|
||||
69 drop: function(event, ui) {
|
||||
70 var element = $(ui.draggable[0]);
|
||||
71 var wrapping = element.attr('id');
|
||||
72 /* alert( $(this).text() + " got " + wrapping); */
|
||||
73 $(ui.draggable[0]).css("background-image","url(book_cover.png)");
|
||||
74 $(ui.draggable[0]).text(books[wrapping]);
|
||||
75 },
|
||||
76 out: function() {
|
||||
77 /* alert( $(this).text() + " lost it" ); */
|
||||
78 }
|
||||
79 });
|
||||
80 $( ".draggable" ).draggable();
|
||||
81 </script>
|
||||
82
|
||||
83 </body>
|
||||
84 </html>
|
||||
```
|
||||
|
||||
### Breaking it down
|
||||
|
||||
Let's go over this code bit by bit.
|
||||
|
||||
* **Lines 1–6:** Upfront, I have the usual HTML boilerplate, `HTML`, `HEAD`, `META`, `TITLE` elements, followed by a link to the CSS for jQuery UI.
|
||||
* **Lines 7–25:** I added two new style classes: `draggable` and `droppable`. These define the layout for the books (draggable) and the people (droppable). Note that, aside from defining the size, background color, padding, and margin, I established that these need to float left. This way, the layout adjusts to the browser window width in a reasonably acceptable form.
|
||||
* **Line 26–27:** With the CSS out of the way, it's time for the JavaScript libraries, first jQuery, then jQuery UI.
|
||||
* **Lines 29–83:** Now that the `HEAD` element is done, next is the `BODY`:
|
||||
* **Lines 30–31:** These couple of titles, `H1` and `H2`, let people know what they're doing here.
|
||||
* **Lines 33–43:** A `DIV` to contain the people:
|
||||
* **Lines 34–42:** The people are defined as droppable `DIV` elements and given `ID` fields corresponding to their names.
|
||||
* **Lines 44–54:** A `DIV` to contain the books:
|
||||
* **Lines 45–53:** The books are defined as draggable `DIV` elements. Each element is declared with a background image corresponding to the wrapping paper with no text between the `<div>` and `</div>`. The `ID` fields correspond to the wrapping paper.
|
||||
* **Lines 56–81:** These contain JavaScript to make it all work.
|
||||
* **Lines 57–67:** This JavaScript object contains the book definitions. The keys (`'bows'`, `'boxes'`, etc.) correspond to the `ID` fields of the book `DIV` elements. The values (`'Untamed by Glennon Doyle',` `"The Heart's Invisible Furies by John Boyne"`, etc.) are the book titles and authors.
|
||||
* **Lines 68–79:** This JavaScript jQuery UI function defines the droppable functionality to be attached to HTML elements whose class is `droppable`.
|
||||
* **Lines 69–75:** When a `draggable` element is dropped onto a `droppable` element, the function `drop` is called.
|
||||
* **Line 70:** The `element` variable is assigned the draggable object that was dropped (this will be a `<div id="..." class="draggable"...></div>` element.
|
||||
* **Line 71:** The `wrapping` variable is assigned the value of the `ID` field in the draggable object.
|
||||
* **Line 72:** This line is commented out, but while I was learning and testing, calls to `alert()` were useful.
|
||||
* **Line 73:** This reassigns the draggable object's background image to a bland image on which text can be read; part 1 of unwrapping is getting rid of the wrapping paper.
|
||||
* **Line 74:** This sets the text of the draggable object to the title of the book, looked up in the book's object using the draggable object's ID; part 2 of the unwrapping is showing the book title and author.
|
||||
* **Lines 76–78:** For a while, I thought I wanted something to happen when a draggable object was removed from a droppable object (e.g., when a club member stole a book), which would require using the `out` function in a droppable object. Eventually, I decided not to do anything. But, this could note that the book was stolen and make it "unstealable" for one turn; or it could show a status line that says something like: _"Wanda's book Blah Blah by Joe Blogs was stolen, and she needs to choose another."_
|
||||
* **Line 80:** This JavaScript jQuery UI function defines the draggable functionality to be attached to HTML elements whose class is `draggable`. In my case, the default behavior was all I needed.
|
||||
|
||||
|
||||
* Lines 1–6: Upfront, I have the usual `HTML` boilerplate, HTML, `HEAD`, `META`, `TITLE` elements, followed by a link to the CSS for jQuery UI.
|
||||
* Lines 7–25: I added two new style classes: `draggable` and `droppable`. These define the layout for the books (draggable) and the people (droppable). Note that, aside from defining the size, background color, padding, and margin, I established that these need to float left. This way, the layout adjusts to the browser window width in a reasonably acceptable form.
|
||||
* Line 26–27: With the CSS out of the way, it's time for the JavaScript libraries, first jQuery, then jQuery UI.
|
||||
* Lines 29–83: Now that the `HEAD` `element` is done, next is the `BODY`:
|
||||
* Lines 30–31: These couple of titles, `H1` and `H2`, let people know what they're doing here.
|
||||
Lines 33–43: A `DIV` to contain the people:
|
||||
Lines 34–42: The people are defined as `droppable` `DIV` elements and given `ID` fields corresponding to their names.
|
||||
Lines 44–54: A `DIV` to contain the books:
|
||||
Lines 45–53: The books are defined as `draggable` `DIV` elements. Each element is declared with a background image corresponding to the `wrapping` paper with no text between the `<div>` and `</div>`. The `ID` fields correspond to the wrapping paper.
|
||||
Lines 56–81: These contain JavaScript to make it all work.
|
||||
* Lines 57–67: This JavaScript object contains the book definitions. The keys ('bows', `'boxes'`, etc.) correspond to the `ID` fields of the book `DIV` elements. The values ('Untamed by Glennon Doyle', `"The Heart's Invisible Furies by John Boyne"`, etc.) are the book titles and authors.
|
||||
Lines 68–79: This JavaScript jQuery UI function defines the `droppable` functionality to be attached to HTML elements whose class is `drop`pable.
|
||||
Lines 69–75: When a `draggable` element is dropped onto a droppable element, the function drop is called.
|
||||
Line 70: The element variable is assigned the draggable object that was dropped (this will be a `<div id="..." class="draggable"...></div>` element.
|
||||
Line 71: The wrapping variable is assigned the value of the `ID` field in the draggable object.
|
||||
Line 72: This line is commented `out`, but while I was learning and testing, calls to `alert()` were useful.
|
||||
* Line 73: This reassigns the draggable object's background image to a bland image on which text can be read; part 1 of unwrapping is getting rid of the wrapping paper.
|
||||
* Line 74: This sets the text of the draggable object to the title of the book, looked up in the book's object using the draggable object's ID; part 2 of the unwrapping is showing the book title and author.
|
||||
Lines 76–78: For a while, I thought I wanted something to happen when a draggable object was removed from a droppable object (e.g., when a club member stole a book), which would require using the out function in a droppable object. Eventually, I decided not to do anything. But, this could note that the book was stolen and make it "unstealable" for one turn; or it could show a status line that says something like: "Wanda's book Blah Blah by Joe Blogs was stolen, and she needs to choose another."
|
||||
Line 80: This JavaScript jQuery UI function defines the draggable functionality to be attached to HTML elements whose class is draggable. In my case, the default behavior was all I needed.
|
||||
|
||||
That's it!
|
||||
|
||||
@ -189,49 +180,38 @@ That's it!
|
||||
|
||||
Libraries like jQuery and jQuery UI are incredibly helpful when trying to do something complicated in JavaScript. Look at the `$().draggable()` and `$().droppable()` functions, for example:
|
||||
|
||||
|
||||
```
|
||||
`$( ".draggable" ).draggable();`
|
||||
$( ".draggable" ).draggable();
|
||||
```
|
||||
|
||||
The `".draggable"` allows associating the `draggable()` function with any HTML element whose class is "draggable." The `draggable()` function comes with all sorts of useful behavior about picking, dragging, and releasing a draggable HTML element.
|
||||
|
||||
If you haven't spent much time with jQuery, I really like the book [_jQuery in Action_][21] by Bear Bibeault, Yehuda Katz, and Aurelio De Rosa. Similarly, [_jQuery UI in Action_][22] by TJ VanToll is a great help with the jQuery UI (where draggable and droppable come from).
|
||||
If you haven't spent much time with jQuery, I really like the book [jQuery in Action][9] by Bear Bibeault, Yehuda Katz, and Aurelio De Rosa. Similarly, [jQuery UI in Action][10] by TJ VanToll is a great help with the jQuery UI (where draggable and droppable come from).
|
||||
|
||||
Of course, there are many other JavaScript libraries, frameworks, and what-nots around to do good stuff in the user interface. I haven't really started to explore all that jQuery and jQuery UI offer, and I want to play around with the rest to see what can be done.
|
||||
|
||||
Image by: (Chris Hermansen, CC BY-SA 4.0)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/1/open-source-gift-exchange
|
||||
|
||||
作者:[Chris Hermansen][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][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/brown-package-red-bow.jpg?itok=oxZYQzH- (Package wrapped with brown paper and red bow)
|
||||
[2]: https://all-free-download.com/free-vector/patterns-creative-commons.html#google_vignette
|
||||
[3]: https://opensource.com/tags/gimp
|
||||
[4]: https://opensource.com/sites/default/files/uploads/bookexchangestart.png (Virtual book exchange)
|
||||
[5]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[6]: https://opensource.com/sites/default/files/uploads/bookexchangeperson1.png (Virtual book exchange)
|
||||
[7]: https://opensource.com/sites/default/files/uploads/bookexchangeperson2.png (Virtual book exchange)
|
||||
[8]: http://december.com/html/4/element/html.html
|
||||
[9]: http://december.com/html/4/element/head.html
|
||||
[10]: http://december.com/html/4/element/meta.html
|
||||
[11]: http://december.com/html/4/element/title.html
|
||||
[12]: http://december.com/html/4/element/link.html
|
||||
[13]: http://december.com/html/4/element/style.html
|
||||
[14]: http://december.com/html/4/element/script.html
|
||||
[15]: https://code.jquery.com/jquery-1.12.4.js"\>\
|
||||
[16]: https://code.jquery.com/ui/1.12.1/jquery-ui.js"\>\
|
||||
[17]: http://december.com/html/4/element/body.html
|
||||
[18]: http://december.com/html/4/element/h1.html
|
||||
[19]: http://december.com/html/4/element/h2.html
|
||||
[20]: http://december.com/html/4/element/div.html
|
||||
[21]: https://www.manning.com/books/jquery-in-action-third-edition
|
||||
[22]: https://www.manning.com/books/jquery-ui-in-action
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/brown-package-red-bow.jpg
|
||||
[2]: https://unsplash.com/@jessbaileydesigns?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
[3]: https://unsplash.com/s/photos/package?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
[4]: https://all-free-download.com/free-vector/patterns-creative-commons.html#google_vignette
|
||||
[5]: https://opensource.com/tags/gimp
|
||||
[6]: https://opensource.com/sites/default/files/uploads/bookexchangestart.png
|
||||
[7]: https://opensource.com/sites/default/files/uploads/bookexchangeperson1.png
|
||||
[8]: https://opensource.com/sites/default/files/uploads/bookexchangeperson2.png
|
||||
[9]: https://www.manning.com/books/jquery-in-action-third-edition
|
||||
[10]: https://www.manning.com/books/jquery-ui-in-action
|
||||
|
@ -1,70 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (Donkey)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (3 stress-free steps to tackling your task list)
|
||||
[#]: via: (https://opensource.com/article/21/1/break-down-tasks)
|
||||
[#]: author: (Kevin Sonney https://opensource.com/users/ksonney)
|
||||
|
||||
3 stress-free steps to tackling your task list
|
||||
======
|
||||
Break your larger tasks into small steps to keep from being overwhelmed.
|
||||
![Team checklist][1]
|
||||
|
||||
In prior years, this annual series covered individual apps. This year, we are looking at all-in-one solutions in addition to strategies to help in 2021. Welcome to day 14 of 21 Days of Productivity in 2021.
|
||||
|
||||
At the start of the week, I like to review my schedule and look at the things I either need or would like to accomplish. And often, there are some items on that list that are relatively big. Whether it is an issue for work, a series of articles on productivity, or maybe an improvement to the chicken enclosures, the task can seem really daunting when taken as a single job. The odds are good that I will not be able to sit down and finish something like (just as an example, mind you) 21 articles in a single block of time, or even a single day.
|
||||
|
||||
![21 Days of Productivity project screenshot][2]
|
||||
|
||||
21 Days of Productivity (Kevin Sonney, [CC BY-SA 4.0][3])
|
||||
|
||||
So the first thing I do when I have something like this on my list is to break it down into smaller pieces. As Nobel laureate [William Faulkner][4] famously said, "The man who removes a mountain begins by carrying away small stones." We need to take our big tasks (the mountain) and find the individual steps (the small stones) that need to be done.
|
||||
|
||||
I use the following steps to break down my big tasks into little ones:
|
||||
|
||||
1. I usually have a fair idea of what needs to be done to complete a task. If not, I do a little research to figure that out.
|
||||
2. I write down the steps I think it will take, in order.
|
||||
3. Finally, I sit down with my calendar and the list and start to spread the tasks out across several days (or weeks, or months) to get an idea of when I might finish it.
|
||||
|
||||
|
||||
|
||||
Now I have not only a plan but an idea of how long it is going to take. As I complete each step, I can see that big task get not only a little smaller but closer to completion.
|
||||
|
||||
There is an old military saying that goes, "No plan survives contact with the enemy." It is almost certain that there will be a point or two (or five) where I realize that something as simple as "take a screenshot" needs to be expanded into something _much_ more complex. In fact, taking the screenshots of [Easy!Appointments][5] turned out to be:
|
||||
|
||||
1. Install and configure Easy!Appointments.
|
||||
2. Install and configure the Easy!Appointments WordPress plugin.
|
||||
3. Generate the API keys needed to sync the calendar.
|
||||
4. Take screenshots.
|
||||
|
||||
|
||||
|
||||
Even then, I had to break these tasks down into smaller pieces—download the software, configure NGINX, validate the installs…you get the idea. And that's OK. A plan, or set of tasks, is not set in stone and can be changed as needed.
|
||||
|
||||
![project completion pie chart][6]
|
||||
|
||||
About 2/3 done for this year! (Kevin Sonney, [CC BY-SA 4.0][3])
|
||||
|
||||
This is a learned skill and will take some effort the first few times. Learning how to break big tasks into smaller steps allows you to track progress towards a goal or completion of something big without getting overwhelmed in the process.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/1/break-down-tasks
|
||||
|
||||
作者:[Kevin Sonney][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/ksonney
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/checklist_todo_clock_time_team.png?itok=1z528Q0y (Team checklist)
|
||||
[2]: https://opensource.com/sites/default/files/day14-image1.png
|
||||
[3]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[4]: https://en.wikipedia.org/wiki/William_Faulkner
|
||||
[5]: https://opensource.com/article/21/1/open-source-scheduler
|
||||
[6]: https://opensource.com/sites/default/files/day14-image2_1.png
|
@ -1,164 +1,151 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Introduction to Thunderbird mail filters)
|
||||
[#]: via: (https://fedoramagazine.org/introduction-to-thunderbird-mail-filters/)
|
||||
[#]: author: (Richard England https://fedoramagazine.org/author/rlengland/)
|
||||
[#]: subject: "Introduction to Thunderbird mail filters"
|
||||
[#]: via: "https://fedoramagazine.org/introduction-to-thunderbird-mail-filters/"
|
||||
[#]: author: "Richard England https://fedoramagazine.org/author/rlengland/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Introduction to Thunderbird mail filters
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
Everyone eventually runs into an inbox loaded with messages that they need to sort through. If you are like a lot of people, this is not a fast process. However, use of mail filters can make the task a little less tedious by letting Thunderbird pre-sort the messages into categories that reflect their source, priority, or usefulness. This article is an introduction to the creation of filters in Thunderbird.
|
||||
|
||||
Filters may be created for each email account you have created in Thunderbird. These are the accounts you see in the main Thunderbird folder pane shown at the left of the “Classic Layout”.
|
||||
|
||||
![Classic Layout][2]
|
||||
![][2]
|
||||
|
||||
There are two methods that can be used to create mail filters for your accounts. The first is based on the currently selected account and the second on the currently selected message. Both are discussed here.
|
||||
|
||||
### Message destination folder
|
||||
|
||||
Before filtering messages there has to be a destination for them. Create the destination by selecting a location to create a new folder. In this example the destination will be **Local Folders** shown in the accounts pane. Right click on **Local Folders** and select _New Folder…_ from the menu.
|
||||
Before filtering messages there has to be a destination for them. Create the destination by selecting a location to create a new folder. In this example the destination will be **Local Folders**shown in the accounts pane. Right click on **Local Folders** and select *New Folder…* from the menu.
|
||||
|
||||
![Creating a new folder][3]
|
||||
![][3]
|
||||
|
||||
Enter the name of the new folder in the menu and select _Create Folder._ The mail to filter is coming from the New York Times so that is the name entered.
|
||||
Enter the name of the new folder in the menu and select *Create Folder.* The mail to filter is coming from the New York Times so that is the name entered.
|
||||
|
||||
![Folder creation][4]
|
||||
![][4]
|
||||
|
||||
### Filter creation based on the selected account
|
||||
|
||||
Select the _Inbox_ for the account you wish to filter and select the toolbar menu item at _Tools > Message_Filters_.
|
||||
Select the *Inbox* for the account you wish to filter and select the toolbar menu item at *Tools > Message_Filters*.
|
||||
|
||||
![Message_Filters menu location][5]
|
||||
![][5]
|
||||
|
||||
The _Message Filters_ menu appears and is set to your pre-selected account as indicated at the top in the selection menu labelled _Filters for:_.
|
||||
The *Message Filters* menu appears and is set to your pre-selected account as indicated at the top in the selection menu labelled *Filters for:*.
|
||||
|
||||
![Message Filters menu][6]
|
||||
![][6]
|
||||
|
||||
Previously created filters, if any, are listed beneath the account name in the “_Filter Name”_ column. To the right of this list are controls that let you modify the filters selected. These controls are activated when you select a filter. More on this later.
|
||||
Previously created filters, if any, are listed beneath the account name in the “*Filter Name”*column. To the right of this list are controls that let you modify the filters selected. These controls are activated when you select a filter. More on this later.
|
||||
|
||||
Start creating your filter as follows:
|
||||
|
||||
1. Verify the correct account has been pre-selected. It may be changed if necessary.
|
||||
2. Select _New…_ from the menu list at the right.
|
||||
1. Verify the correct account has been pre-selected. It may be changed if necessary.
|
||||
2. Select New… from the menu list at the right.
|
||||
|
||||
|
||||
|
||||
When you select _New_ you will see the _Filter Rules_ menu where you define your filter. Note that when using _New…_ you have the option to copy an existing filter to use as a template or to simply duplicate the settings.
|
||||
When you select *New* you will see the *Filter Rules*menu where you define your filter. Note that when using *New…* you have the option to copy an existing filter to use as a template or to simply duplicate the settings.
|
||||
|
||||
Filter rules are made up of three things, the “property” to be tested, the “test”, and the “value” to be tested against. Once the condition is met, the “action” is performed.
|
||||
|
||||
![Message Filters menu][7]
|
||||
![][7]
|
||||
|
||||
Complete this filter as follows:
|
||||
|
||||
1. Enter an appropriate name in the textbox labelled _Filter name:_
|
||||
2. Select the property _From_ in the left drop down menu, if not set.
|
||||
3. Leave the test set to _contains_.
|
||||
4. Enter the value, in this case the email address of the sender.
|
||||
1. Enter an appropriate name in the textbox labelled Filter name:
|
||||
2. Select the property From in the left drop down menu, if not set.
|
||||
3. Leave the test set to contains.
|
||||
4. Enter the value, in this case the email address of the sender.
|
||||
|
||||
Under the *Perform these actions:* section at the bottom, create an action rule to move the message and choose the destination.
|
||||
|
||||
1. Select Move Messages to from the left end of the action line.
|
||||
2. Select Choose Folder… and select Local Folders > New York Times.
|
||||
3. Select OK.
|
||||
|
||||
Under the _Perform these actions:_ section at the bottom, create an action rule to move the message and choose the destination.
|
||||
By default the **Apply filter when:** is set to *Manually Run* and *Getting New Mail:*. This means that when new mail appears in the Inbox for this account the filter will be applied and you may run it manually at any time, if necessary. There are other options available but they are too numerous to be discussed in this introduction. They are, however, for the most part self explanatory.
|
||||
|
||||
1. Select _Move Messages to_ from the left end of the action line.
|
||||
2. Select _Choose Folder…_ and select _Local Folders > New York Times_.
|
||||
3. Select _OK_.
|
||||
If more than one rule or action is to be created during the same session, the “+” to the right of each entry provides that option. Additional property, test, and value entries can be added. If more than one rule is created, make certain that the appropriate option for *Match all of the following* and *Match any of the following* is selected. In this example the choice does not matter since we are only setting one filter.
|
||||
|
||||
After selecting *OK,*the *Message Filters* menu is displayed again showing your newly created filter. Note that the menu items on the right side of the menu are now active for *Edit…* and *Delete.*
|
||||
|
||||
![][8]
|
||||
|
||||
By default the **Apply filter when:** is set to _Manually Run_ and _Getting New Mail:_. This means that when new mail appears in the Inbox for this account the filter will be applied and you may run it manually at any time, if necessary. There are other options available but they are too numerous to be discussed in this introduction. They are, however, for the most part self explanatory.
|
||||
Also notice the message *“Enabled filters are run automatically in the order shown below”*. If there are multiple filters the order is changed by selecting the one to be moved and using the *Move to Top, Move Up, Move Down,*or*Move to Bottom* buttons. The order can change the destination of your messages so consider the tests used in each filter carefully when deciding the order.
|
||||
|
||||
If more than one rule or action is to be created during the same session, the “+” to the right of each entry provides that option. Additional property, test, and value entries can be added. If more than one rule is created, make certain that the appropriate option for _Match all of the following_ and _Match any of the following_ is selected. In this example the choice does not matter since we are only setting one filter.
|
||||
|
||||
After selecting _OK,_ the _Message Filters_ menu is displayed again showing your newly created filter. Note that the menu items on the right side of the menu are now active for _Edit…_ and _Delete._
|
||||
|
||||
![First filter in the Message Filters menu][8]
|
||||
|
||||
Also notice the message _“Enabled filters are run automatically in the order shown below”_. If there are multiple filters the order is changed by selecting the one to be moved and using the _Move to Top, Move Up, Move Down,_ or _Move to Bottom_ buttons. The order can change the destination of your messages so consider the tests used in each filter carefully when deciding the order.
|
||||
|
||||
Since you have just created this filter you may wish to use the _Run Now_ button to run your newly created filter on the Inbox shown to the left of the button.
|
||||
Since you have just created this filter you may wish to use the *Run Now* button to run your newly created filter on the Inbox shown to the left of the button.
|
||||
|
||||
### Filter creation based on a message
|
||||
|
||||
An alternative creation technique is to select a message from the message pane and use the _Create Filter From Message…_ option from the menu bar.
|
||||
An alternative creation technique is to select a message from the message pane and use the *Create Filter From Message…* option from the menu bar.
|
||||
|
||||
In this example the filter will use two rules to select the messages: the email address and a text string in the Subject line of the email. Start as follows:
|
||||
In this example the filter will use two rules to select the messages: the email address and a text string in the Subject line of the email. Start as follows:
|
||||
|
||||
1. Select a message in the message page.
|
||||
2. Select the filter options on the toolbar at _Message > Create Filter From Message…_.
|
||||
1. Select a message in the message page.
|
||||
2. Select the filter options on the toolbar at Message > Create Filter From Message….
|
||||
|
||||
![][9]
|
||||
|
||||
|
||||
![Create new filters from Messages][9]
|
||||
|
||||
The pre-selected message, highlighted in grey in the message pane above, determines the account used and _Create Filter From Message…_ takes you directly to the _Filter Rules_ menu.
|
||||
The pre-selected message, highlighted in grey in the message pane above, determines the account used and *Create Filter From Message…* takes you directly to the *Filter Rules* menu.
|
||||
|
||||
![][10]
|
||||
|
||||
The property (_From_), test (_is_), and value (email) are pre-set for you as shown in the image above. Complete this filter as follows:
|
||||
The property (*From*), test (*is*), and value (email) are pre-set for you as shown in the image above. Complete this filter as follows:
|
||||
|
||||
1. Enter an appropriate name in the textbox labelled _Filter name:_. _COVID_ is the name in this case.
|
||||
2. Check that the property is _From_.
|
||||
3. Verify the test is set to _is_.
|
||||
4. Confirm that the value for the email address is from the correct sender.
|
||||
5. Select the “+” to the right of the _From_ rule to create a new filter rule.
|
||||
6. In the new rule, change the default property entry _From_ to _Subject_ using the pulldown menu.
|
||||
7. Set the test to _contains_.
|
||||
8. Enter the value text to be matched in the Email “Subject” line. In this case _COVID_.
|
||||
1. Enter an appropriate name in the textbox labelled Filter name:. COVID is the name in this case.
|
||||
2. Check that the property is From.
|
||||
3. Verify the test is set to is.
|
||||
4. Confirm that the value for the email address is from the correct sender.
|
||||
5. Select the “+” to the right of the From rule to create a new filter rule.
|
||||
6. In the new rule, change the default property entry From to Subject using the pulldown menu.
|
||||
7. Set the test to contains.
|
||||
8. Enter the value text to be matched in the Email “Subject” line. In this case COVID.
|
||||
|
||||
Since we left the *Match all of the following* item checked, each message will be from the address chosen AND will have the text *COVID* in the email subject line.
|
||||
|
||||
Now use the action rule to choose the destination for the messages under the *Perform these actions:* section at the bottom:
|
||||
|
||||
Since we left the _Match all of the following_ item checked, each message will be from the address chosen AND will have the text _COVID_ in the email subject line.
|
||||
1. Select Move Messages to from the left menu.
|
||||
2. Select Choose Folder… and select Local Folders > COVID in Scotland. (This destination was created before this example was started. There was no magic here.)
|
||||
3. Select OK.
|
||||
|
||||
Now use the action rule to choose the destination for the messages under the _Perform these actions:_ section at the bottom:
|
||||
|
||||
1. Select _Move Messages to_ from the left menu.
|
||||
2. Select _Choose Folder…_ and select _Local Folders > COVID in Scotland_. (This destination was created before this example was started. There was no magic here.)
|
||||
3. Select _OK_.
|
||||
|
||||
|
||||
|
||||
_OK_ will cause the _Message Filters_ menu to appear, again, verifying that the new filter has been created.
|
||||
*OK* will cause the *Message Filters* menu to appear, again, verifying that the new filter has been created.
|
||||
|
||||
### The Message Filters menu
|
||||
|
||||
All the message filters you create will appear in the _Message Filters_ menu. Recall that the _Message Filters_ is available in the menu bar at _Tools > Message Filters_.
|
||||
All the message filters you create will appear in the *Message Filters* menu. Recall that the *Message Filters* is available in the menu bar at *Tools > Message Filters*.
|
||||
|
||||
Once you have created filters there are several options to manage them. To change a filter, select the filter in question and click on the _Edit_ button. This will take you back to the _Filter Rules_ menu for that filter. As mentioned earlier, you can change the order in which the rules are apply here using the _Move_ buttons. Disable a filter by clicking on the check mark in the _Enabled_ column.
|
||||
Once you have created filters there are several options to manage them. To change a filter, select the filter in question and click on the *Edit* button. This will take you back to the *Filter Rules* menu for that filter. As mentioned earlier, you can change the order in which the rules are apply here using the *Move* buttons. Disable a filter by clicking on the check mark in the *Enabled* column.
|
||||
|
||||
![][11]
|
||||
|
||||
The _Run Now_ button will execute the selected filter immediately. You may also run your filter from the menu bar using _Tools > Run Filters on Folder_ or _Tools > Run Filters on Message_.
|
||||
The *Run Now* button will execute the selected filter immediately. You may also run your filter from the menu bar using *Tools > Run Filters on Folder* or *Tools > Run Filters on Message*.
|
||||
|
||||
### Next step
|
||||
|
||||
This article hasn’t covered every feature available for message filtering but hopefully it provides enough information for you to get started. Places for further investigation are the “property”, “test”, and “actions” in the _Filter menu_ as well as the settings there for when your filter is to be run, _Archiving, After Sending,_ and _Periodically_.
|
||||
This article hasn’t covered every feature available for message filtering but hopefully it provides enough information for you to get started. Places for further investigation are the “property”, “test”, and “actions” in the *Filter menu* as well as the settings there for when your filter is to be run, *Archiving, After Sending,* and *Periodically*.
|
||||
|
||||
### References
|
||||
|
||||
Mozilla: [Organize][12] [Your Messages][12] [by Using Filters][12]
|
||||
Mozilla: [Organize][12][Your Messages][13][by Using Filters][14]
|
||||
|
||||
MozillaZine: [Message][13] [Filters][13]
|
||||
MozillaZine: [Message][15][Filters][16]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/introduction-to-thunderbird-mail-filters/
|
||||
|
||||
作者:[Richard England][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][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/rlengland/
|
||||
[b]: https://github.com/lujun9972
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2021/01/Tbird_mail_filters-1-816x345.jpg
|
||||
[2]: https://fedoramagazine.org/wp-content/uploads/2021/01/Image_001-1024x613.png
|
||||
[3]: https://fedoramagazine.org/wp-content/uploads/2021/01/Image_New_Folder.png
|
||||
@ -171,4 +158,7 @@ via: https://fedoramagazine.org/introduction-to-thunderbird-mail-filters/
|
||||
[10]: https://fedoramagazine.org/wp-content/uploads/2021/01/Filter_rules_2-1.png
|
||||
[11]: https://fedoramagazine.org/wp-content/uploads/2021/01/Message_Filters_2nd_entry.png
|
||||
[12]: https://support.mozilla.org/en-US/kb/organize-your-messages-using-filters
|
||||
[13]: http://kb.mozillazine.org/Filters_%28Thunderbird%29
|
||||
[13]: https://support.mozilla.org/en-US/kb/organize-your-messages-using-filters
|
||||
[14]: https://support.mozilla.org/en-US/kb/organize-your-messages-using-filters
|
||||
[15]: http://kb.mozillazine.org/Filters_%28Thunderbird%29
|
||||
[16]: http://kb.mozillazine.org/Filters_%28Thunderbird%29
|
||||
|
@ -1,46 +1,43 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Interview with Shuah Khan, Kernel Maintainer & Linux Fellow)
|
||||
[#]: via: (https://www.linux.com/news/interview-with-shuah-khan-kernel-maintainer-linux-fellow/)
|
||||
[#]: author: (The Linux Foundation https://www.linuxfoundation.org/en/blog/interview-with-shuah-khan-kernel-maintainer-linux-fellow/)
|
||||
[#]: subject: "Interview with Shuah Khan, Kernel Maintainer & Linux Fellow"
|
||||
[#]: via: "https://www.linux.com/news/interview-with-shuah-khan-kernel-maintainer-linux-fellow/"
|
||||
[#]: author: "The Linux Foundation https://www.linuxfoundation.org/en/blog/interview-with-shuah-khan-kernel-maintainer-linux-fellow/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Interview with Shuah Khan, Kernel Maintainer & Linux Fellow
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
_Jason Perlow, Director of Project Insights and Editorial Content at the Linux Foundation, had an opportunity to speak with Shuah Khan about her experiences as a woman in the technology industry. She discusses how mentorship can improve the overall diversity and makeup of open source projects, why software maintainers are important for the health of open source projects such as the Linux kernel, and how language inclusivity and codes of conduct can improve relationships and communication between software maintainers and individual contributors._
|
||||
Jason Perlow, Director of Project Insights and Editorial Content at the Linux Foundation, had an opportunity to speak with Shuah Khan about her experiences as a woman in the technology industry. She discusses how mentorship can improve the overall diversity and makeup of open source projects, why software maintainers are important for the health of open source projects such as the Linux kernel, and how language inclusivity and codes of conduct can improve relationships and communication between software maintainers and individual contributors.
|
||||
|
||||
**JP:** So, Shuah, I know you wear many different hats at the Linux Foundation. What do you call yourself around here these days?
|
||||
|
||||
**SK:** <laughs> Well, I primarily call myself a Kernel Maintainer & Linux Fellow. In addition to that, I focus on two areas that are important to the continued health and sustainability of the open source projects in the Linux ecosystem. The first one is bringing more women into the Kernel community, and additionally, I am leading the mentorship program efforts overall at the Linux Foundation. And in that role, in addition to the Linux Kernel Mentorship, we are looking at how the Linux Foundation mentorship program is working overall, how it is scaling. I make sure the [LFX Mentorship][2] platform scales and serves diverse mentees and mentors’ needs in this role.
|
||||
**SK:** <laughs> Well, I primarily call myself a Kernel Maintainer & Linux Fellow. In addition to that, I focus on two areas that are important to the continued health and sustainability of the open source projects in the Linux ecosystem. The first one is bringing more women into the Kernel community, and additionally, I am leading the mentorship program efforts overall at the Linux Foundation. And in that role, in addition to the Linux Kernel Mentorship, we are looking at how the Linux Foundation mentorship program is working overall, how it is scaling. I make sure the [LFX Mentorship][1] platform scales and serves diverse mentees and mentors’ needs in this role.
|
||||
|
||||
The LF mentorships program includes several projects in the Linux kernel, LFN, HyperLedger, Open MainFrame, OpenHPC, and other technologies. [The Linux Foundation’s Mentorship Programs][3] are designed to help developers with the necessary skills–many of whom are first-time open source contributors–experiment, learn, and contribute effectively to open source communities.
|
||||
The LF mentorships program includes several projects in the Linux kernel, LFN, HyperLedger, Open MainFrame, OpenHPC, and other technologies. [The Linux Foundation’s Mentorship Programs][2] are designed to help developers with the necessary skills–many of whom are first-time open source contributors–experiment, learn, and contribute effectively to open source communities.
|
||||
|
||||
The mentorship program has been successful in its mission to train new developers and make these talented pools of prospective employees trained by experts to employers. Several graduated mentees have found jobs. New developers have improved the quality and security of various open source projects, including the Linux kernel. Several Linux kernel bugs were fixed, a new subsystem mentor was added, and a new driver maintainer is now part of the Linux kernel community. My sincere thanks to all our mentors for volunteering to share their expertise.
|
||||
|
||||
**JP:** How long have you been working on the Kernel?
|
||||
|
||||
**SK:** Since 2010, or 2011, I got involved in the [Android Mainlining project][4]. My [first patch removed the Android pmem driver][5].
|
||||
**SK:** Since 2010, or 2011, I got involved in the [Android Mainlining project][3]. My [first patch removed the Android pmem driver][4].
|
||||
|
||||
**JP:** Wow! Is there any particular subsystem that you specialize in?
|
||||
|
||||
**SK:** I am a self described generalist. I maintain the [kernel self-test][6] subsystem, the [USB over IP driver][7], [usbip tool][8], and the [cpupower][9] tool. I contributed to the media subsystem working on [Media Controller Device Allocator API][10] to resolve shared device resource management problems across device drivers from different subsystems.
|
||||
**SK:** I am a self described generalist. I maintain the [kernel self-test][5] subsystem, the [USB over IP driver][6], [usbip tool][7], and the [cpupower][8] tool. I contributed to the media subsystem working on [Media Controller Device Allocator API][9] to resolve shared device resource management problems across device drivers from different subsystems.
|
||||
|
||||
**JP:** Hey, I’ve [actually used the USB over IP driver][11] when I worked at Microsoft on Azure. And also, when I’ve used AWS and Google Compute.
|
||||
**JP:** Hey, I’ve [actually used the USB over IP driver][10] when I worked at Microsoft on Azure. And also, when I’ve used AWS and Google Compute.
|
||||
|
||||
**SK:** It’s a small niche driver used in cloud computing. Docker and other containers use that driver heavily. That’s how they provide remote access to USB devices on the server to export devices to be imported by other systems for use.
|
||||
|
||||
**JP:** I initially used it for IoT kinds of stuff in the embedded systems space. Were you the original lead developer on it, or was it one of those things you fell into because nobody else was maintaining it?
|
||||
|
||||
**SK:** Well, twofold. I was looking at USB over IP because I like that technology. it just so happened the driver was brought from the staging tree into the Mainline kernel, I volunteered at the time to maintain it. Over the last few years, we discovered some security issues with it, because it handles a lot of userspace data, so I had a lot of fun fixing all of those. <laugh>.
|
||||
**SK:** Well, twofold. I was looking at USB over IP because I like that technology. it just so happened the driver was brought from the staging tree into the Mainline kernel, I volunteered at the time to maintain it. Over the last few years, we discovered some security issues with it, because it handles a lot of userspace data, so I had a lot of fun fixing all of those. <laugh>.
|
||||
|
||||
**JP:** What drew you into the Linux operating system, and what drew you into the kernel development community in the first place?
|
||||
|
||||
**SK:** Well, I have been doing kernel development for a very long time. I worked on the [LynxOS RTOS][12], a while back, and then HP/UX, when I was working at HP, after which I transitioned into doing open source development — the [OpenHPI][13] project, to support HP’s rack server hardware, and that allowed me to work much more closely with Linux on the back end. And at some point, I decided I wanted to work with the kernel and become part of the Linux kernel community. I started as an independent contributor.
|
||||
**SK:** Well, I have been doing kernel development for a very long time. I worked on the [LynxOS RTOS][11], a while back, and then HP/UX, when I was working at HP, after which I transitioned into doing open source development — the [OpenHPI][12] project, to support HP’s rack server hardware, and that allowed me to work much more closely with Linux on the back end. And at some point, I decided I wanted to work with the kernel and become part of the Linux kernel community. I started as an independent contributor.
|
||||
|
||||
**JP:** Maybe it just displays my own ignorance, but you are the first female, hardcore Linux kernel developer I have ever met. I mean, I had met female core OS developers before — such as when I was at Microsoft and IBM — but not for Linux. Why do you suppose we lack women and diversity in general when participating in open source and the technology industry overall?
|
||||
|
||||
@ -52,9 +49,9 @@ There’s a natural resistance to choosing certain professions that you have to
|
||||
|
||||
**SK:** Yes.
|
||||
|
||||
**JP:** It’s funny; my wife really likes this [Netflix show about matchmaking in India][14]. Are you familiar with it?
|
||||
**JP:** It’s funny; my wife really likes this [Netflix show about matchmaking in India][13]. Are you familiar with it?
|
||||
|
||||
**SK:** <laughs> Yes I enjoyed the series, and [A Suitable Girl][15] documentary film that follows three women as they navigate making decisions about their careers and family obligations.
|
||||
**SK:** <laughs> Yes I enjoyed the series, and [A Suitable Girl][14] documentary film that follows three women as they navigate making decisions about their careers and family obligations.
|
||||
|
||||
**JP:** For many Americans, this is our first introduction to what home life is like for Indian people. But many of the women featured on this show are professionals, such as doctors, lawyers, and engineers. And they are very ambitious, but of course, the family tries to set them up in a marriage to find a husband for them that is compatible. As a result, you get to learn about the traditional values and roles they still want women to play there — while at the same time, many women are coming out of higher learning institutions in that country that are seeking technical careers.
|
||||
|
||||
@ -62,11 +59,11 @@ There’s a natural resistance to choosing certain professions that you have to
|
||||
|
||||
**JP:** Women in technical and STEM professions are becoming much more prominent in other countries, such as China, Japan, and Korea. For some reason, in the US, I tend to see more women enter the medical profession than hard technology — and it might be a level of effort and perceived reward thing. You can spend eight years becoming a medical doctor or eight years becoming a scientist or an engineer, and it can be equally difficult, but the compensation at the end may not be the same. It’s expensive to get an education, and it takes a long time and hard work, regardless of the professional discipline.
|
||||
|
||||
**SK:** I have also heard that women also like to enter professions where they can make a difference in the world — a human touch, if you will. So that may translate to them choosing careers where they can make a larger impact on people — and they may view careers in technology as not having those same attributes. Maybe when we think about attracting women to technology fields, we might have to promote technology aspects that make a difference. That may be changing now, such as the [LF Public Health][16] (LFPH) project we kicked off last year. And with [LF AI & Data Foundation][17], we are also making a difference in people’s lives, such as [detecting earthquakes][18] or [analyzing climate change][19]. If we were to promote projects such as these, we might draw more women in.
|
||||
**SK:** I have also heard that women also like to enter professions where they can make a difference in the world — a human touch, if you will. So that may translate to them choosing careers where they can make a larger impact on people — and they may view careers in technology as not having those same attributes. Maybe when we think about attracting women to technology fields, we might have to promote technology aspects that make a difference. That may be changing now, such as the [LF Public Health][15] (LFPH) project we kicked off last year. And with [LF AI & Data Foundation][16], we are also making a difference in people’s lives, such as [detecting earthquakes][17] or [analyzing climate change][18]. If we were to promote projects such as these, we might draw more women in.
|
||||
|
||||
**JP:** So clearly, one of the areas of technology where you can make a difference is in open source, as the LF is hosting some very high-concept and existential types of projects such as [LF Energy][20], for example — I had no idea what was involved in it and what its goals were until I spoke to [Shuli Goodman][21] in-depth about it. With the mentorship program, I assume we need this to attract fresh talent — because as folks like us get older and retire, and they exit the field, we need new people to replace them. So I assume mentorship, for the Linux Foundation, is an investment in our own technologies, correct?
|
||||
**JP:** So clearly, one of the areas of technology where you can make a difference is in open source, as the LF is hosting some very high-concept and existential types of projects such as [LF Energy][19], for example — I had no idea what was involved in it and what its goals were until I spoke to [Shuli Goodman][20] in-depth about it. With the mentorship program, I assume we need this to attract fresh talent — because as folks like us get older and retire, and they exit the field, we need new people to replace them. So I assume mentorship, for the Linux Foundation, is an investment in our own technologies, correct?
|
||||
|
||||
**SK:** Correct. Bringing in new developers into the fold is the primary purpose, of course — and at the same time, I view the LF as taking on mentorship provides that neutral, level playing field across the industry for all open source projects. Secondly, we offer a self-service platform, [LFX Mentorship][22], where anyone can come in and start their project. So when the COVID-19 pandemic began, we [expanded this program to help displaced people][3] — students, et cetera, and less visible projects. Not all projects typically get as much funding or attention as others do — such as a Kubernetes or Linux kernel — among the COVID mentorship program projects we are funding. I am particularly proud of supporting a climate change-related project, [Using Machine Learning to Predict Deforestation][23].
|
||||
**SK:** Correct. Bringing in new developers into the fold is the primary purpose, of course — and at the same time, I view the LF as taking on mentorship provides that neutral, level playing field across the industry for all open source projects. Secondly, we offer a self-service platform, [LFX Mentorship][21], where anyone can come in and start their project. So when the COVID-19 pandemic began, we [expanded this program to help displaced people][22] — students, et cetera, and less visible projects. Not all projects typically get as much funding or attention as others do — such as a Kubernetes or Linux kernel — among the COVID mentorship program projects we are funding. I am particularly proud of supporting a climate change-related project, [Using Machine Learning to Predict Deforestation][23].
|
||||
|
||||
The self-service approach allows us to fund and add new developers to projects where they are needed. The LF mentorships are remote work opportunities that are accessible to developers around the globe. We see people sign up for mentorship projects from places we haven’t seen before, such as Africa, and so on, thus creating a level playing field.
|
||||
|
||||
@ -122,43 +119,43 @@ Talking about backpacking reminded me of the two-day, 22-mile backpacking trip d
|
||||
|
||||
**JP:** Awesome. I enjoyed talking to you today. So happy I finally got to meet you virtually.
|
||||
|
||||
The post [Interview with Shuah Khan, Kernel Maintainer & Linux Fellow][33] appeared first on [Linux Foundation][34].
|
||||
The post [Interview with Shuah Khan, Kernel Maintainer & Linux Fellow][33] appeared first on [Linux Foundation][34].
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/news/interview-with-shuah-khan-kernel-maintainer-linux-fellow/
|
||||
|
||||
作者:[The Linux Foundation][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.linuxfoundation.org/en/blog/interview-with-shuah-khan-kernel-maintainer-linux-fellow/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.linux.com/wp-content/uploads/2021/01/3E9C3E02-5F59-4A99-AD4A-814C7B8737A9_1_105_c.jpeg
|
||||
[2]: https://lfx.linuxfoundation.org/tools/mentorship/
|
||||
[3]: https://linuxfoundation.org/about/diversity-inclusivity/mentorship/
|
||||
[4]: https://elinux.org/Android_Mainlining_Project
|
||||
[5]: https://lkml.org/lkml/2012/1/26/368
|
||||
[6]: https://www.kernel.org/doc/html/v4.15/dev-tools/kselftest.html
|
||||
[7]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/usb/usbip
|
||||
[8]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/usb/usbip
|
||||
[9]: https://www.systutorials.com/docs/linux/man/1-cpupower/
|
||||
[10]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/media/mc/mc-dev-allocator.c
|
||||
[11]: https://www.linux-magazine.com/Issues/2018/208/Tutorial-USB-IP
|
||||
[12]: https://en.wikipedia.org/wiki/LynxOS
|
||||
[13]: http://www.openhpi.org/Developers
|
||||
[14]: https://www.netflix.com/title/80244565
|
||||
[15]: https://en.wikipedia.org/wiki/A_Suitable_Girl_(film)
|
||||
[16]: https://www.lfph.io/
|
||||
[17]: https://lfaidata.foundation/
|
||||
[18]: https://openeew.com/
|
||||
[19]: https://www.os-climate.org/
|
||||
[20]: https://www.lfenergy.org/
|
||||
[21]: mailto:sgoodman@contractor.linuxfoundation.org
|
||||
[22]: https://mentorship.lfx.linuxfoundation.org/
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://lfx.linuxfoundation.org/tools/mentorship/
|
||||
[2]: https://linuxfoundation.org/about/diversity-inclusivity/mentorship/
|
||||
[3]: https://elinux.org/Android_Mainlining_Project
|
||||
[4]: https://lkml.org/lkml/2012/1/26/368
|
||||
[5]: https://www.kernel.org/doc/html/v4.15/dev-tools/kselftest.html
|
||||
[6]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/usb/usbip
|
||||
[7]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/usb/usbip
|
||||
[8]: https://www.systutorials.com/docs/linux/man/1-cpupower/
|
||||
[9]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/media/mc/mc-dev-allocator.c
|
||||
[10]: https://www.linux-magazine.com/Issues/2018/208/Tutorial-USB-IP
|
||||
[11]: https://en.wikipedia.org/wiki/LynxOS
|
||||
[12]: http://www.openhpi.org/Developers
|
||||
[13]: https://www.netflix.com/title/80244565
|
||||
[14]: https://en.wikipedia.org/wiki/A_Suitable_Girl_(film)
|
||||
[15]: https://www.lfph.io/
|
||||
[16]: https://lfaidata.foundation/
|
||||
[17]: https://openeew.com/
|
||||
[18]: https://www.os-climate.org/
|
||||
[19]: https://www.lfenergy.org/
|
||||
[20]: https://www.linux.com/mailto:sgoodman@contractor.linuxfoundation.org
|
||||
[21]: https://mentorship.lfx.linuxfoundation.org/
|
||||
[22]: https://linuxfoundation.org/about/diversity-inclusivity/mentorship/
|
||||
[23]: https://mentorship.lfx.linuxfoundation.org/project/926665ac-9b96-45aa-bb11-5d99096be870
|
||||
[24]: https://www.linuxfoundation.org/en/blog/preventing-supply-chain-attacks-like-solarwinds/
|
||||
[25]: https://www.linuxfoundation.org/en/press-release/new-open-source-contributor-report-from-linux-foundation-and-harvard-identifies-motivations-and-opportunities-for-improving-software-security/
|
@ -92,7 +92,7 @@ The system is trying to remove an item that does not exist in the basket, and it
|
||||
|
||||
```
|
||||
public int RemoveItem(Hashtable item) {
|
||||
if(basket.IndexOf(item) >= 0) {
|
||||
if(basket.IndexOf(item) >= 0) {
|
||||
basket.RemoveAt(basket.IndexOf(item));
|
||||
}
|
||||
return basket.Count;
|
||||
|
@ -86,7 +86,7 @@ Implement this processing logic in the `ShippingAPI` class:
|
||||
```
|
||||
private double Calculate10PercentDiscount(double total) {
|
||||
double discount = 0.00;
|
||||
if(total > 500.00) {
|
||||
if(total > 500.00) {
|
||||
discount = (total/100) * 10;
|
||||
}
|
||||
return discount;
|
||||
|
@ -42,7 +42,7 @@ nvme_core.default_ps_max_latency_us=0
|
||||
|
||||
In the end I upgraded my main workstation so I could repurpose its existing Samsung EVO 960 for the HoneyComb which worked much better.
|
||||
|
||||
After some fidgeting I was able to install Fedora but it became apparent that the integrated network ports still don’t work with the mainline kernel. The NXP tech is great but requires a custom kernel build and tooling. Some earlier blogs got around this with a USB->RJ45 Ethernet adapter which works fine. Hopefully network support will be mainlined soon, but for now I snagged a kernel SRPM from the helpful engineers on Discord. With the custom kernel the 1Gbe NIC worked fine, but it turns out the SFP+ ports need more configuration. They won’t be recognized as interfaces until you use NXP’s _restool_ utility to map ports to their usage. In this case just a runtime mapping of _dmap -> dni_ was required. This is NXP’s way of mapping a MAC to a network interface via IOCTL commands. The restool binary isn’t provided either and must be built from source. It then layers on management scripts which use cheeky $arg0 references for redirection to call the restool binary with complex arguments.
|
||||
After some fidgeting I was able to install Fedora but it became apparent that the integrated network ports still don’t work with the mainline kernel. The NXP tech is great but requires a custom kernel build and tooling. Some earlier blogs got around this with a USB->RJ45 Ethernet adapter which works fine. Hopefully network support will be mainlined soon, but for now I snagged a kernel SRPM from the helpful engineers on Discord. With the custom kernel the 1Gbe NIC worked fine, but it turns out the SFP+ ports need more configuration. They won’t be recognized as interfaces until you use NXP’s _restool_ utility to map ports to their usage. In this case just a runtime mapping of _dmap -> dni_ was required. This is NXP’s way of mapping a MAC to a network interface via IOCTL commands. The restool binary isn’t provided either and must be built from source. It then layers on management scripts which use cheeky $arg0 references for redirection to call the restool binary with complex arguments.
|
||||
|
||||
Since I was starting to accumulate quite a few custom packages it was apparent that a COPR repo was needed to simplify this for Fedora. If you’re not familiar with COPR I think it’s one of Fedora’s finest resources. This repo contains the uefi build (currently failing build), 5.10.5 kernel built with network support, and the restool binary with supporting scripts. I also added a oneshot systemd unit to enable the SFP+ ports on boot:
|
||||
```
|
||||
|
@ -85,8 +85,8 @@ Describe the newly created namespace:
|
||||
```
|
||||
[root@master ~]# kubectl describe namespace test
|
||||
Name: test
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
Status: Active
|
||||
No resource quota.
|
||||
No LimitRange resource.
|
||||
@ -233,8 +233,8 @@ Verify the Roles:
|
||||
```
|
||||
$ kubectl describe roles -n test
|
||||
Name: list-deployments
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
PolicyRule:
|
||||
Resources Non-Resource URLs Resource Names Verbs
|
||||
--------- ----------------- -------------- -----
|
||||
|
@ -1,57 +1,51 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Draw Mandelbrot fractals with GIMP scripting)
|
||||
[#]: via: (https://opensource.com/article/21/2/gimp-mandelbrot)
|
||||
[#]: author: (Cristiano L. Fontana https://opensource.com/users/cristianofontana)
|
||||
[#]: subject: "Draw Mandelbrot fractals with GIMP scripting"
|
||||
[#]: via: "https://opensource.com/article/21/2/gimp-mandelbrot"
|
||||
[#]: author: "Cristiano L. Fontana https://opensource.com/users/cristianofontana"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Draw Mandelbrot fractals with GIMP scripting
|
||||
======
|
||||
Create complex mathematical images with GIMP's Script-Fu language.
|
||||
|
||||
![Painting art on a computer screen][1]
|
||||
|
||||
Image by: Opensource.com
|
||||
|
||||
The GNU Image Manipulation Program ([GIMP][2]) is my go-to solution for image editing. Its toolset is very powerful and convenient, except for doing [fractals][3], which is one thing you cannot draw by hand easily. These are fascinating mathematical constructs that have the characteristic of being [self-similar][4]. In other words, if they are magnified in some areas, they will look remarkably similar to the unmagnified picture. Besides being interesting, they also make very pretty pictures!
|
||||
|
||||
![Portion of a Mandelbrot fractal using GIMPs Coldfire palette][5]
|
||||
![Rotated and magnified portion of the Mandelbrot set using Firecode][5]
|
||||
|
||||
Portion of a Mandelbrot fractal using GIMP's Coldfire palette (Cristiano Fontana, [CC BY-SA 4.0][6])
|
||||
GIMP can be automated with [Script-Fu][6] to do [batch processing of images][7] or create complicated procedures that are not practical to do by hand; drawing fractals falls in the latter category. This tutorial will show how to draw a representation of the [Mandelbrot fractal][8] using GIMP and Script-Fu.
|
||||
|
||||
GIMP can be automated with [Script-Fu][7] to do [batch processing of images][8] or create complicated procedures that are not practical to do by hand; drawing fractals falls in the latter category. This tutorial will show how to draw a representation of the [Mandelbrot fractal][9] using GIMP and Script-Fu.
|
||||
![Mandelbrot set drawn using GIMP's Firecode palette][9]
|
||||
|
||||
![Mandelbrot set drawn using GIMP's Firecode palette][10]
|
||||
|
||||
Portion of a Mandelbrot fractal using GIMP's Firecode palette. (Cristiano Fontana, [CC BY-SA 4.0][6])
|
||||
|
||||
![Rotated and magnified portion of the Mandelbrot set using Firecode.][11]
|
||||
|
||||
Rotated and magnified portion of the Mandelbrot set using the Firecode palette. (Cristiano Fontana, [CC BY-SA 4.0][6])
|
||||
![Rotated and magnified portion of the Mandelbrot set using Firecode.][10]
|
||||
|
||||
In this tutorial, you will write a script that creates a layer in an image and draws a representation of the Mandelbrot set with a colored environment around it.
|
||||
|
||||
### What is the Mandelbrot set?
|
||||
|
||||
Do not panic! I will not go into too much detail here. For the more math-savvy, the Mandelbrot set is defined as the set of [complex numbers][12] _a_ for which the succession
|
||||
Do not panic! I will not go into too much detail here. For the more math-savvy, the Mandelbrot set is defined as the set of [complex numbers][11] *a* for which the succession
|
||||
|
||||
_zn+1 = zn2 + a_
|
||||
zn+1 = zn2 + a
|
||||
|
||||
does not diverge when starting from _z₀ = 0_.
|
||||
does not diverge when starting from *z₀ = 0*.
|
||||
|
||||
In reality, the Mandelbrot set is the fancy-looking black blob in the pictures; the nice-looking colors are outside the set. They represent how many iterations are required for the magnitude of the succession of numbers to pass a threshold value. In other words, the color scale shows how many steps are required for the succession to pass an upper-limit value.
|
||||
|
||||
### GIMP's Script-Fu
|
||||
|
||||
[Script-Fu][7] is the scripting language built into GIMP. It is an implementation of the [Scheme programming language][13].
|
||||
[Script-Fu][12] is the scripting language built into GIMP. It is an implementation of the [Scheme programming language][13].
|
||||
|
||||
If you want to get more acquainted with Scheme, GIMP's documentation offers an [in-depth tutorial][14]. I also wrote an article about [batch processing images][8] using Script-Fu. Finally, the Help menu offers a Procedure Browser with very extensive documentation with all of Script-Fu's functions described in detail.
|
||||
If you want to get more acquainted with Scheme, GIMP's documentation offers an [in-depth tutorial][14]. I also wrote an article about [batch processing images][15] using Script-Fu. Finally, the Help menu offers a Procedure Browser with very extensive documentation with all of Script-Fu's functions described in detail.
|
||||
|
||||
![GIMP Procedure Browser][15]
|
||||
|
||||
(Cristiano Fontana, [CC BY-SA 4.0][6])
|
||||
|
||||
Scheme is a Lisp-like language, so a major characteristic is that it uses a [prefix notation][16] and a [lot of parentheses][17]. Functions and operators are applied to a list of operands by prefixing them:
|
||||
![GIMP Procedure Browser][16]
|
||||
|
||||
Scheme is a Lisp-like language, so a major characteristic is that it uses a [prefix notation][17] and a [lot of parentheses][18]. Functions and operators are applied to a list of operands by prefixing them:
|
||||
|
||||
```
|
||||
(function-name operand operand ...)
|
||||
@ -67,7 +61,6 @@ Scheme is a Lisp-like language, so a major characteristic is that it uses a [pre
|
||||
|
||||
You can write your first script and save it to the **Scripts** folder found in the preferences window under **Folders → Scripts**. Mine is at `$HOME/.config/GIMP/2.10/scripts`. Write a file called `mandelbrot.scm` with:
|
||||
|
||||
|
||||
```
|
||||
; Complex numbers implementation
|
||||
(define (make-rectangular x y) (cons x y))
|
||||
@ -111,17 +104,17 @@ You can write your first script and save it to the **Scripts** folder found in t
|
||||
(define bytes-per-pixel (car (gimp-drawable-bpp drawable)))
|
||||
|
||||
; Fractal drawing section.
|
||||
; Code from: <https://rosettacode.org/wiki/Mandelbrot\_set\#Racket>
|
||||
; Code from: https://rosettacode.org/wiki/Mandelbrot_set#Racket
|
||||
(define (iterations a z i)
|
||||
(let ((z′ (add-c (mul-c z z) a)))
|
||||
(if (or (= i num-colors) (> (magnitude z′) threshold))
|
||||
(if (or (= i num-colors) (> (magnitude z′) threshold))
|
||||
i
|
||||
(iterations a z′ (+ i 1)))))
|
||||
|
||||
(define (iter->color i)
|
||||
(if (>= i num-colors)
|
||||
(list->vector '(0 0 0))
|
||||
(list->vector (vector-ref colors i))))
|
||||
(define (iter->color i)
|
||||
(if (>= i num-colors)
|
||||
(list->vector '(0 0 0))
|
||||
(list->vector (vector-ref colors i))))
|
||||
|
||||
(define z0 (make-rectangular 0 0))
|
||||
|
||||
@ -130,10 +123,10 @@ You can write your first script and save it to the **Scripts** folder found in t
|
||||
(real-y (- (* domain-height (/ y height)) offset-y))
|
||||
(a (make-rectangular real-x real-y))
|
||||
(i (iterations a z0 0))
|
||||
(color (iter->color i)))
|
||||
(cond ((and (< x end-x) (< y end-y)) (gimp-drawable-set-pixel drawable x y bytes-per-pixel color)
|
||||
(color (iter->color i)))
|
||||
(cond ((and (< x end-x) (< y end-y)) (gimp-drawable-set-pixel drawable x y bytes-per-pixel color)
|
||||
(loop (+ x 1) end-x y end-y))
|
||||
((and (>= x end-x) (< y end-y)) (gimp-progress-update (/ y end-y))
|
||||
((and (>= x end-x) (< y end-y)) (gimp-progress-update (/ y end-y))
|
||||
(loop 0 end-x (+ y 1) end-y)))))
|
||||
(loop 0 width 0 height)
|
||||
|
||||
@ -161,15 +154,14 @@ You can write your first script and save it to the **Scripts** folder found in t
|
||||
SF-ADJUSTMENT "X offset" '(2.25 -20 20 0.1 1 4 0)
|
||||
SF-ADJUSTMENT "Y offset" '(1.50 -20 20 0.1 1 4 0)
|
||||
)
|
||||
(script-fu-menu-register "script-fu-mandelbrot" "<Image>/Layer/")
|
||||
(script-fu-menu-register "script-fu-mandelbrot" "<Image>/Layer/")
|
||||
```
|
||||
|
||||
I will go through the script to show you what it does.
|
||||
|
||||
### Get ready to draw the fractal
|
||||
|
||||
Since this image is all about complex numbers, I wrote a quick and dirty implementation of complex numbers in Script-Fu. I defined the complex numbers as [pairs][18] of real numbers. Then I added the few functions needed for the script. I used [Racket's documentation][19] as inspiration for function names and roles:
|
||||
|
||||
Since this image is all about complex numbers, I wrote a quick and dirty implementation of complex numbers in Script-Fu. I defined the complex numbers as [pairs][19] of real numbers. Then I added the few functions needed for the script. I used [Racket's documentation][20] as inspiration for function names and roles:
|
||||
|
||||
```
|
||||
(define (make-rectangular x y) (cons x y))
|
||||
@ -198,7 +190,6 @@ Since this image is all about complex numbers, I wrote a quick and dirty impleme
|
||||
|
||||
The new function is called `script-fu-mandelbrot`. The best practice for writing a new function is to call it `script-fu-something` so that it can be identified in the Procedure Browser easily. The function requires a few parameters: an `image` to which it will add a layer with the fractal, the `palette-name` identifying the color palette to be used, the `threshold` value to stop the iteration, the `domain-width` and `domain-height` that identify the image boundaries, and the `offset-x` and `offset-y` to center the image to the desired feature. The script also needs some other parameters that it can deduce from the GIMP interface:
|
||||
|
||||
|
||||
```
|
||||
(define (script-fu-mandelbrot image palette-name threshold domain-width domain-height offset-x offset-y)
|
||||
(define num-colors (car (gimp-palette-get-info palette-name)))
|
||||
@ -212,7 +203,6 @@ The new function is called `script-fu-mandelbrot`. The best practice for writing
|
||||
|
||||
Then it creates a new layer and identifies it as the script's `drawable`. A "drawable" is the element you want to draw on:
|
||||
|
||||
|
||||
```
|
||||
(define new-layer (car (gimp-layer-new image
|
||||
width height
|
||||
@ -226,27 +216,25 @@ Then it creates a new layer and identifies it as the script's `drawable`. A "dra
|
||||
(define bytes-per-pixel (car (gimp-drawable-bpp drawable)))
|
||||
```
|
||||
|
||||
For the code determining the pixels' color, I used the [Racket][20] example on the [Rosetta Code][21] website. It is not the most optimized algorithm, but it is simple to understand. Even a non-mathematician like me can understand it. The `iterations` function determines how many steps the succession requires to pass the threshold value. To cap the iterations, I am using the number of colors in the palette. In other words, if the threshold is too high or the succession does not grow, the calculation stops at the `num-colors` value. The `iter->color` function transforms the number of iterations into a color using the provided palette. If the iteration number is equal to `num-colors`, it uses black because this means that the succession is probably bound and that pixel is in the Mandelbrot set:
|
||||
|
||||
For the code determining the pixels' color, I used the [Racket][21] example on the [Rosetta Code][22] website. It is not the most optimized algorithm, but it is simple to understand. Even a non-mathematician like me can understand it. The `iterations` function determines how many steps the succession requires to pass the threshold value. To cap the iterations, I am using the number of colors in the palette. In other words, if the threshold is too high or the succession does not grow, the calculation stops at the `num-colors` value. The `iter->color` function transforms the number of iterations into a color using the provided palette. If the iteration number is equal to `num-colors`, it uses black because this means that the succession is probably bound and that pixel is in the Mandelbrot set:
|
||||
|
||||
```
|
||||
; Fractal drawing section.
|
||||
; Code from: <https://rosettacode.org/wiki/Mandelbrot\_set\#Racket>
|
||||
; Code from: https://rosettacode.org/wiki/Mandelbrot_set#Racket
|
||||
(define (iterations a z i)
|
||||
(let ((z′ (add-c (mul-c z z) a)))
|
||||
(if (or (= i num-colors) (> (magnitude z′) threshold))
|
||||
(if (or (= i num-colors) (> (magnitude z′) threshold))
|
||||
i
|
||||
(iterations a z′ (+ i 1)))))
|
||||
|
||||
(define (iter->color i)
|
||||
(if (>= i num-colors)
|
||||
(list->vector '(0 0 0))
|
||||
(list->vector (vector-ref colors i))))
|
||||
(define (iter->color i)
|
||||
(if (>= i num-colors)
|
||||
(list->vector '(0 0 0))
|
||||
(list->vector (vector-ref colors i))))
|
||||
```
|
||||
|
||||
Because I have the feeling that Scheme users do not like to use loops, I implemented the function looping over the pixels as a recursive function. The `loop` function reads the starting coordinates and their upper boundaries. At each pixel, it defines some temporary variables with the `let*` function: `real-x` and `real-y` are the real coordinates of the pixel in the complex plane, according to the parameters; the `a` variable is the starting point for the succession; the `i` is the number of iterations; and finally `color` is the pixel color. Each pixel is colored with the `gimp-drawable-set-pixel` function that is an internal GIMP procedure. The peculiarity is that it is not undoable, and it does not trigger the image to refresh. Therefore, the image will not be updated during the operation. To play nice with the user, at the end of each row of pixels, it calls the `gimp-progress-update` function, which updates a progress bar in the user interface:
|
||||
|
||||
|
||||
```
|
||||
(define z0 (make-rectangular 0 0))
|
||||
|
||||
@ -255,17 +243,16 @@ Because I have the feeling that Scheme users do not like to use loops, I impleme
|
||||
(real-y (- (* domain-height (/ y height)) offset-y))
|
||||
(a (make-rectangular real-x real-y))
|
||||
(i (iterations a z0 0))
|
||||
(color (iter->color i)))
|
||||
(cond ((and (< x end-x) (< y end-y)) (gimp-drawable-set-pixel drawable x y bytes-per-pixel color)
|
||||
(color (iter->color i)))
|
||||
(cond ((and (< x end-x) (< y end-y)) (gimp-drawable-set-pixel drawable x y bytes-per-pixel color)
|
||||
(loop (+ x 1) end-x y end-y))
|
||||
((and (>= x end-x) (< y end-y)) (gimp-progress-update (/ y end-y))
|
||||
((and (>= x end-x) (< y end-y)) (gimp-progress-update (/ y end-y))
|
||||
(loop 0 end-x (+ y 1) end-y)))))
|
||||
(loop 0 width 0 height)
|
||||
```
|
||||
|
||||
At the calculation's end, the function needs to inform GIMP that it modified the `drawable`, and it should refresh the interface because the image is not "automagically" updated during the script's execution:
|
||||
|
||||
|
||||
```
|
||||
(gimp-drawable-update drawable 0 0 width height)
|
||||
(gimp-displays-flush)
|
||||
@ -275,7 +262,6 @@ At the calculation's end, the function needs to inform GIMP that it modified the
|
||||
|
||||
To use the `script-fu-mandelbrot` function in the graphical user interface (GUI), the script needs to inform GIMP. The `script-fu-register` function informs GIMP about the parameters required by the script and provides some documentation:
|
||||
|
||||
|
||||
```
|
||||
(script-fu-register
|
||||
"script-fu-mandelbrot" ; Function name
|
||||
@ -300,71 +286,69 @@ To use the `script-fu-mandelbrot` function in the graphical user interface (GUI)
|
||||
|
||||
Then the script tells GIMP to put the new function in the Layer menu with the label "Create a Mandelbrot layer":
|
||||
|
||||
|
||||
```
|
||||
`(script-fu-menu-register "script-fu-mandelbrot" "<Image>/Layer/")`
|
||||
(script-fu-menu-register "script-fu-mandelbrot" "<Image>/Layer/")
|
||||
```
|
||||
|
||||
Having registered the function, you can visualize it in the Procedure Browser.
|
||||
|
||||
![script-fu-mandelbrot function][22]
|
||||
|
||||
(Cristiano Fontana, [CC BY-SA 4.0][6])
|
||||
![script-fu-mandelbrot function][23]
|
||||
|
||||
### Run the script
|
||||
|
||||
Now that the function is ready and registered, you can draw the Mandelbrot fractal! First, create a square image and run the script from the Layers menu.
|
||||
|
||||
![script running][23]
|
||||
|
||||
(Cristiano Fontana, [CC BY-SA 4.0][6])
|
||||
![script running][24]
|
||||
|
||||
The default values are a good starting set to obtain the following image. The first time you run the script, create a very small image (e.g., 60x60 pixels) because this implementation is slow! It took several hours for my computer to create the following image in full 1920x1920 pixels. As I mentioned earlier, this is not the most optimized algorithm; rather, it was the easiest for me to understand.
|
||||
|
||||
![Mandelbrot set drawn using GIMP's Firecode palette][10]
|
||||
|
||||
Portion of a Mandelbrot fractal using GIMP's Firecode palette. (Cristiano Fontana, [CC BY-SA 4.0][6])
|
||||
![Mandelbrot set drawn using GIMP's Firecode palette][25]
|
||||
|
||||
### Learn more
|
||||
|
||||
This tutorial showed how to use GIMP's built-in scripting features to draw an image created with an algorithm. These images show GIMP's powerful set of tools that can be used for artistic applications and mathematical images.
|
||||
|
||||
If you want to move forward, I suggest you look at the official documentation and its [tutorial][14]. As an exercise, try modifying this script to draw a [Julia set][24], and please share the resulting image in the comments.
|
||||
If you want to move forward, I suggest you look at the official documentation and its [tutorial][26]. As an exercise, try modifying this script to draw a [Julia set][27], and please share the resulting image in the comments.
|
||||
|
||||
Image by: Rotated and magnified portion of the Mandelbrot set using Firecode. (Cristiano Fontana, CC BY-SA 4.0)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/2/gimp-mandelbrot
|
||||
|
||||
作者:[Cristiano L. Fontana][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][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/painting_computer_screen_art_design_creative.png?itok=LVAeQx3_ (Painting art on a computer screen)
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/painting_computer_screen_art_design_creative.png
|
||||
[2]: https://www.gimp.org/
|
||||
[3]: https://en.wikipedia.org/wiki/Fractal
|
||||
[4]: https://en.wikipedia.org/wiki/Self-similarity
|
||||
[5]: https://opensource.com/sites/default/files/uploads/mandelbrot_portion.png (Portion of a Mandelbrot fractal using GIMPs Coldfire palette)
|
||||
[6]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[7]: https://docs.gimp.org/en/gimp-concepts-script-fu.html
|
||||
[8]: https://opensource.com/article/21/1/gimp-scripting
|
||||
[9]: https://en.wikipedia.org/wiki/Mandelbrot_set
|
||||
[10]: https://opensource.com/sites/default/files/uploads/mandelbrot.png (Mandelbrot set drawn using GIMP's Firecode palette)
|
||||
[11]: https://opensource.com/sites/default/files/uploads/mandelbrot_portion2.png (Rotated and magnified portion of the Mandelbrot set using Firecode.)
|
||||
[12]: https://en.wikipedia.org/wiki/Complex_number
|
||||
[5]: https://opensource.com/sites/default/files/uploads/mandelbrot_portion.png
|
||||
[6]: https://docs.gimp.org/en/gimp-concepts-script-fu.html
|
||||
[7]: https://opensource.com/article/21/1/gimp-scripting
|
||||
[8]: https://en.wikipedia.org/wiki/Mandelbrot_set
|
||||
[9]: https://opensource.com/sites/default/files/uploads/mandelbrot.png
|
||||
[10]: https://opensource.com/sites/default/files/uploads/mandelbrot_portion2.png
|
||||
[11]: https://en.wikipedia.org/wiki/Complex_number
|
||||
[12]: https://docs.gimp.org/en/gimp-concepts-script-fu.html
|
||||
[13]: https://en.wikipedia.org/wiki/Scheme_(programming_language)
|
||||
[14]: https://docs.gimp.org/en/gimp-using-script-fu-tutorial.html
|
||||
[15]: https://opensource.com/sites/default/files/uploads/procedure_browser_0.png (GIMP Procedure Browser)
|
||||
[16]: https://en.wikipedia.org/wiki/Polish_notation
|
||||
[17]: https://xkcd.com/297/
|
||||
[18]: https://www.gnu.org/software/guile/manual/html_node/Pairs.html
|
||||
[19]: https://docs.racket-lang.org/reference/generic-numbers.html?q=make-rectangular#%28part._.Complex_.Numbers%29
|
||||
[20]: https://racket-lang.org/
|
||||
[21]: https://rosettacode.org/wiki/Mandelbrot_set#Racket
|
||||
[22]: https://opensource.com/sites/default/files/uploads/mandelbrot_documentation.png (script-fu-mandelbrot function)
|
||||
[23]: https://opensource.com/sites/default/files/uploads/script_working.png (script running)
|
||||
[24]: https://en.wikipedia.org/wiki/Julia_set
|
||||
[15]: https://opensource.com/article/21/1/gimp-scripting
|
||||
[16]: https://opensource.com/sites/default/files/uploads/procedure_browser_0.png
|
||||
[17]: https://en.wikipedia.org/wiki/Polish_notation
|
||||
[18]: https://xkcd.com/297/
|
||||
[19]: https://www.gnu.org/software/guile/manual/html_node/Pairs.html
|
||||
[20]: https://docs.racket-lang.org/reference/generic-numbers.html?q=make-rectangular#%28part._.Complex_.Numbers%29
|
||||
[21]: https://racket-lang.org/
|
||||
[22]: https://rosettacode.org/wiki/Mandelbrot_set#Racket
|
||||
[23]: https://opensource.com/sites/default/files/uploads/mandelbrot_documentation.png
|
||||
[24]: https://opensource.com/sites/default/files/uploads/script_working.png
|
||||
[25]: https://opensource.com/sites/default/files/uploads/mandelbrot.png
|
||||
[26]: https://docs.gimp.org/en/gimp-using-script-fu-tutorial.html
|
||||
[27]: https://en.wikipedia.org/wiki/Julia_set
|
||||
|
@ -1,18 +1,20 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (A step-by-step guide to Knative eventing)
|
||||
[#]: via: (https://opensource.com/article/21/2/knative-eventing)
|
||||
[#]: author: (Jessica Cherry https://opensource.com/users/cherrybomb)
|
||||
[#]: subject: "A step-by-step guide to Knative eventing"
|
||||
[#]: via: "https://opensource.com/article/21/2/knative-eventing"
|
||||
[#]: author: "Jessica Cherry https://opensource.com/users/cherrybomb"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
A step-by-step guide to Knative eventing
|
||||
======
|
||||
Knative eventing is a way to create, send, and verify events in your
|
||||
cloud-native environment.
|
||||
Knative eventing is a way to create, send, and verify events in your cloud-native environment.
|
||||
|
||||
![Computer laptop in space][1]
|
||||
|
||||
Image by: Opensource.com
|
||||
|
||||
In a previous article, I covered [how to create a small app with Knative][2], which is an open source project that adds components to [Kubernetes][3] for deploying, running, and managing [serverless, cloud-native][4] applications. In this article, I'll explain Knative eventing, a way to create, send, and verify events in your cloud-native environment.
|
||||
|
||||
Events can be generated from many sources in your environment, and they can be confusing to manage or define. Since Knative follows the [CloudEvents][5] specification, it allows you to have one common abstraction point for your environment, where the events are defined to one specification.
|
||||
@ -25,7 +27,6 @@ This walkthrough uses [Minikube][7] with Kubernetes 1.19.0. It also makes some c
|
||||
|
||||
**Minikube pre-configuration commands:**
|
||||
|
||||
|
||||
```
|
||||
$ minikube config set kubernetes-version v1.19.0
|
||||
$ minikube config set memory 4000
|
||||
@ -34,7 +35,6 @@ $ minikube config set cpus 4
|
||||
|
||||
Before starting Minikube, run the following commands to make sure your configuration stays and start Minikube:
|
||||
|
||||
|
||||
```
|
||||
$ minikube delete
|
||||
$ minikube start
|
||||
@ -44,9 +44,8 @@ $ minikube start
|
||||
|
||||
Install the Knative eventing custom resource definitions (CRDs) using kubectl. The following shows the command and a snippet of the output:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl apply --filename <https://github.com/knative/eventing/releases/download/v0.20.0/eventing-crds.yaml>
|
||||
$ kubectl apply --filename https://github.com/knative/eventing/releases/download/v0.20.0/eventing-crds.yaml
|
||||
|
||||
customresourcedefinition.apiextensions.k8s.io/apiserversources.sources.knative.dev created
|
||||
customresourcedefinition.apiextensions.k8s.io/brokers.eventing.knative.dev created
|
||||
@ -56,9 +55,8 @@ customresourcedefinition.apiextensions.k8s.io/triggers.eventing.knative.dev crea
|
||||
|
||||
Next, install the core components using kubectl:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl apply --filename <https://github.com/knative/eventing/releases/download/v0.20.0/eventing-core.yaml>
|
||||
$ kubectl apply --filename https://github.com/knative/eventing/releases/download/v0.20.0/eventing-core.yaml
|
||||
namespace/knative-eventing created
|
||||
serviceaccount/eventing-controller created
|
||||
clusterrolebinding.rbac.authorization.k8s.io/eventing-controller created
|
||||
@ -66,23 +64,20 @@ clusterrolebinding.rbac.authorization.k8s.io/eventing-controller created
|
||||
|
||||
Since you're running a standalone version of the Knative eventing service, you must install the in-memory channel to pass events. Using kubectl, run:
|
||||
|
||||
|
||||
```
|
||||
`$ kubectl apply --filename https://github.com/knative/eventing/releases/download/v0.20.0/in-memory-channel.yaml`
|
||||
$ kubectl apply --filename https://github.com/knative/eventing/releases/download/v0.20.0/in-memory-channel.yaml
|
||||
```
|
||||
|
||||
Install the broker, which utilizes the channels and runs the event routing:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl apply --filename <https://github.com/knative/eventing/releases/download/v0.20.0/mt-channel-broker.yaml>
|
||||
$ kubectl apply --filename https://github.com/knative/eventing/releases/download/v0.20.0/mt-channel-broker.yaml
|
||||
clusterrole.rbac.authorization.k8s.io/knative-eventing-mt-channel-broker-controller created
|
||||
clusterrole.rbac.authorization.k8s.io/knative-eventing-mt-broker-filter created
|
||||
```
|
||||
|
||||
Next, create a namespace and add a small broker to it; this broker routes events to triggers. Create your namespace using kubectl:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl create namespace eventing-test
|
||||
namespace/eventing-test created
|
||||
@ -90,7 +85,6 @@ namespace/eventing-test created
|
||||
|
||||
Now create a small broker named `default` in your namespace. The following is the YAML from my **broker.yaml** file (which can be found in my GitHub repository):
|
||||
|
||||
|
||||
```
|
||||
apiVersion: eventing.knative.dev/v1
|
||||
kind: broker
|
||||
@ -101,7 +95,6 @@ metadata:
|
||||
|
||||
Then apply your broker file using kubectl:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl create -f broker.yaml
|
||||
broker.eventing.knative.dev/default created
|
||||
@ -109,11 +102,10 @@ $ kubectl create -f broker.yaml
|
||||
|
||||
Verify that everything is up and running (you should see the confirmation output) after you run the command:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl -n eventing-test get broker default
|
||||
NAME URL AGE READY REASON
|
||||
default <http://broker-ingress.knative-eventing.svc.cluster.local/eventing-test/default> 3m6s True
|
||||
default http://broker-ingress.knative-eventing.svc.cluster.local/eventing-test/default 3m6s True
|
||||
```
|
||||
|
||||
You'll need this URL from the broker output later for sending events, so save it.
|
||||
@ -126,7 +118,6 @@ First, you need to create event consumers. You'll create two consumers in this w
|
||||
|
||||
**The hello-display YAML code:**
|
||||
|
||||
|
||||
```
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@ -135,7 +126,7 @@ metadata:
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels: &labels
|
||||
matchLabels: &labels
|
||||
app: hello-display
|
||||
template:
|
||||
metadata:
|
||||
@ -145,7 +136,7 @@ spec:
|
||||
- name: event-display
|
||||
image: gcr.io/knative-releases/knative.dev/eventing-contrib/cmd/event_display
|
||||
|
||||
\---
|
||||
---
|
||||
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
@ -162,7 +153,6 @@ spec:
|
||||
|
||||
**The goodbye-display YAML code:**
|
||||
|
||||
|
||||
```
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@ -171,7 +161,7 @@ metadata:
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels: &labels
|
||||
matchLabels: &labels
|
||||
app: goodbye-display
|
||||
template:
|
||||
metadata:
|
||||
@ -179,10 +169,10 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: event-display
|
||||
# Source code: <https://github.com/knative/eventing-contrib/tree/master/cmd/event\_display>
|
||||
# Source code: https://github.com/knative/eventing-contrib/tree/master/cmd/event_display
|
||||
image: gcr.io/knative-releases/knative.dev/eventing-contrib/cmd/event_display
|
||||
|
||||
\---
|
||||
---
|
||||
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
@ -199,7 +189,6 @@ spec:
|
||||
|
||||
The differences in the YAML between the two consumers are in the `app` and `metadata name` sections. While both consumers are on the same ports, you can target one when generating an event. Create the consumers using kubectl:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl -n eventing-test apply -f hello-display.yaml
|
||||
deployment.apps/hello-display created
|
||||
@ -212,7 +201,6 @@ service/goodbye-display created
|
||||
|
||||
Check to make sure the deployments are running after you've applied the YAML files:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl -n eventing-test get deployments hello-display goodbye-display
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
@ -226,7 +214,6 @@ Now, you need to create the triggers, which define the events the consumer recei
|
||||
|
||||
**The greeting-trigger.yaml code:**
|
||||
|
||||
|
||||
```
|
||||
apiVersion: eventing.knative.dev/v1
|
||||
kind: Trigger
|
||||
@ -246,7 +233,6 @@ spec:
|
||||
|
||||
To create the first trigger, apply your YAML file:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl -n eventing-test apply -f greeting-trigger.yaml
|
||||
trigger.eventing.knative.dev/hello-display created
|
||||
@ -256,7 +242,6 @@ Next, make the second trigger using **sendoff-trigger.yaml**. This sends anythin
|
||||
|
||||
**The sendoff-trigger.yaml code:**
|
||||
|
||||
|
||||
```
|
||||
apiVersion: eventing.knative.dev/v1
|
||||
kind: Trigger
|
||||
@ -276,7 +261,6 @@ spec:
|
||||
|
||||
Next, apply your second trigger definition to the cluster:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl -n eventing-test apply -f sendoff-trigger.yaml
|
||||
trigger.eventing.knative.dev/goodbye-display created
|
||||
@ -284,19 +268,17 @@ trigger.eventing.knative.dev/goodbye-display created
|
||||
|
||||
Confirm everything is correctly in place by getting your triggers from the cluster using kubectl:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl -n eventing-test get triggers
|
||||
NAME BROKER SUBSCRIBER_URI AGE READY
|
||||
goodbye-display default <http://goodbye-display.eventing-test.svc.cluster.local/> 24s True
|
||||
hello-display default <http://hello-display.eventing-test.svc.cluster.local/> 46s True
|
||||
NAME BROKER SUBSCRIBER_URI AGE READY
|
||||
goodbye-display default http://goodbye-display.eventing-test.svc.cluster.local/ 24s True
|
||||
hello-display default http://hello-display.eventing-test.svc.cluster.local/ 46s True
|
||||
```
|
||||
|
||||
### Create an event producer
|
||||
|
||||
Create a pod you can use to send events. This is a simple pod deployment with curl and SSH access for you to [send events using curl][8]. Because the broker can be accessed only from inside the cluster where Knative eventing is installed, the pod needs to be in the cluster; this is the only way to send events into the cluster. Use the **event-producer.yaml** file with this code:
|
||||
|
||||
|
||||
```
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
@ -318,7 +300,6 @@ spec:
|
||||
|
||||
Next, deploy the pod by using kubectl:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl -n eventing-test apply -f event-producer.yaml
|
||||
pod/curl created
|
||||
@ -326,7 +307,6 @@ pod/curl created
|
||||
|
||||
To verify, get the deployment and make sure the pod is up and running:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl get pods -n eventing-test
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
@ -339,14 +319,12 @@ Since this article has been so configuration-heavy, I imagine you'll be happy to
|
||||
|
||||
Begin by logging into the pod:
|
||||
|
||||
|
||||
```
|
||||
`$ kubectl -n eventing-test attach curl -it`
|
||||
$ kubectl -n eventing-test attach curl -it
|
||||
```
|
||||
|
||||
Once logged in, you'll see output similar to:
|
||||
|
||||
|
||||
```
|
||||
Defaulting container name to curl.
|
||||
Use 'kubectl describe pod/curl -n eventing-test' to see all of the containers in this pod.
|
||||
@ -356,9 +334,8 @@ If you don't see a command prompt, try pressing enter.
|
||||
|
||||
Now, generate an event using curl. This needs some extra definitions and requires the broker URL generated during the installation. This example sends a greeting to the broker:
|
||||
|
||||
|
||||
```
|
||||
curl -v "<http://broker-ingress.knative-eventing.svc.cluster.local/eventing-test/default>" \
|
||||
curl -v "http://broker-ingress.knative-eventing.svc.cluster.local/eventing-test/default" \
|
||||
-X POST \
|
||||
-H "Ce-Id: say-hello" \
|
||||
-H "Ce-Specversion: 1.0" \
|
||||
@ -372,31 +349,29 @@ curl -v "<http://broker-ingress.knative-eventing.svc.cluster.local/eventing-test
|
||||
|
||||
When you run the command, this should be the output (and you should receive a [202 Accepted][10] response):
|
||||
|
||||
|
||||
```
|
||||
> POST /eventing-test/default HTTP/1.1
|
||||
> User-Agent: curl/7.35.0
|
||||
> Host: broker-ingress.knative-eventing.svc.cluster.local
|
||||
> Accept: */*
|
||||
> Ce-Id: say-hello
|
||||
> Ce-Specversion: 1.0
|
||||
> Ce-Type: greeting
|
||||
> Ce-Source: not-sendoff
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 24
|
||||
>
|
||||
< HTTP/1.1 202 Accepted
|
||||
< Date: Sun, 24 Jan 2021 22:25:25 GMT
|
||||
< Content-Length: 0
|
||||
> POST /eventing-test/default HTTP/1.1
|
||||
> User-Agent: curl/7.35.0
|
||||
> Host: broker-ingress.knative-eventing.svc.cluster.local
|
||||
> Accept: */*
|
||||
> Ce-Id: say-hello
|
||||
> Ce-Specversion: 1.0
|
||||
> Ce-Type: greeting
|
||||
> Ce-Source: not-sendoff
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 24
|
||||
>
|
||||
< HTTP/1.1 202 Accepted
|
||||
< Date: Sun, 24 Jan 2021 22:25:25 GMT
|
||||
< Content-Length: 0
|
||||
```
|
||||
|
||||
The 202 means the trigger sent it to the **hello-display** consumer (because of the definition.)
|
||||
|
||||
Next, send a second definition to the **goodbye-display** consumer with this new curl command:
|
||||
|
||||
|
||||
```
|
||||
curl -v "<http://broker-ingress.knative-eventing.svc.cluster.local/eventing-test/default>" \
|
||||
curl -v "http://broker-ingress.knative-eventing.svc.cluster.local/eventing-test/default" \
|
||||
-X POST \
|
||||
-H "Ce-Id: say-goodbye" \
|
||||
-H "Ce-Specversion: 1.0" \
|
||||
@ -410,22 +385,21 @@ This time, it is a `sendoff` and not a greeting based on the previous setup sect
|
||||
|
||||
Your output should look like this, with another 202 returned:
|
||||
|
||||
|
||||
```
|
||||
> POST /eventing-test/default HTTP/1.1
|
||||
> User-Agent: curl/7.35.0
|
||||
> Host: broker-ingress.knative-eventing.svc.cluster.local
|
||||
> Accept: */*
|
||||
> Ce-Id: say-goodbye
|
||||
> Ce-Specversion: 1.0
|
||||
> Ce-Type: not-greeting
|
||||
> Ce-Source: sendoff
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 26
|
||||
>
|
||||
< HTTP/1.1 202 Accepted
|
||||
< Date: Sun, 24 Jan 2021 22:33:00 GMT
|
||||
< Content-Length: 0
|
||||
> POST /eventing-test/default HTTP/1.1
|
||||
> User-Agent: curl/7.35.0
|
||||
> Host: broker-ingress.knative-eventing.svc.cluster.local
|
||||
> Accept: */*
|
||||
> Ce-Id: say-goodbye
|
||||
> Ce-Specversion: 1.0
|
||||
> Ce-Type: not-greeting
|
||||
> Ce-Source: sendoff
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 26
|
||||
>
|
||||
< HTTP/1.1 202 Accepted
|
||||
< Date: Sun, 24 Jan 2021 22:33:00 GMT
|
||||
< Content-Length: 0
|
||||
```
|
||||
|
||||
Congratulations, you sent two events!
|
||||
@ -438,14 +412,12 @@ Now that the events have been sent, how do you know that the correct consumers r
|
||||
|
||||
Start with the **hello-display** consumer::
|
||||
|
||||
|
||||
```
|
||||
`$ kubectl -n eventing-test logs -l app=hello-display --tail=100`
|
||||
$ kubectl -n eventing-test logs -l app=hello-display --tail=100
|
||||
```
|
||||
|
||||
There isn't much running in this example cluster, so you should see only one event:
|
||||
|
||||
|
||||
```
|
||||
☁️ cloudevents.Event
|
||||
Validation: valid
|
||||
@ -467,7 +439,6 @@ You've confirmed the **hello-display** consumer received the event! Now check th
|
||||
|
||||
Start by running the same command but with **goodbye-display**:
|
||||
|
||||
|
||||
```
|
||||
$ kubectl -n eventing-test logs -l app=goodbye-display --tail=100
|
||||
☁️ cloudevents.Event
|
||||
@ -494,9 +465,8 @@ So you sent events to each consumer using curl, but what if you want to send an
|
||||
|
||||
Here is a curl example of a definition for sending an event to both consumers:
|
||||
|
||||
|
||||
```
|
||||
curl -v "<http://broker-ingress.knative-eventing.svc.cluster.local/eventing-test/default>" \
|
||||
curl -v "http://broker-ingress.knative-eventing.svc.cluster.local/eventing-test/default" \
|
||||
-X POST \
|
||||
-H "Ce-Id: say-hello-goodbye" \
|
||||
-H "Ce-Specversion: 1.0" \
|
||||
@ -512,27 +482,25 @@ Here is sample output of what the events look like after they are sent.
|
||||
|
||||
**Output of the event being sent:**
|
||||
|
||||
|
||||
```
|
||||
> POST /eventing-test/default HTTP/1.1
|
||||
> User-Agent: curl/7.35.0
|
||||
> Host: broker-ingress.knative-eventing.svc.cluster.local
|
||||
> Accept: */*
|
||||
> Ce-Id: say-hello-goodbye
|
||||
> Ce-Specversion: 1.0
|
||||
> Ce-Type: greeting
|
||||
> Ce-Source: sendoff
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 41
|
||||
>
|
||||
< HTTP/1.1 202 Accepted
|
||||
< Date: Sun, 24 Jan 2021 23:04:15 GMT
|
||||
< Content-Length: 0
|
||||
> POST /eventing-test/default HTTP/1.1
|
||||
> User-Agent: curl/7.35.0
|
||||
> Host: broker-ingress.knative-eventing.svc.cluster.local
|
||||
> Accept: */*
|
||||
> Ce-Id: say-hello-goodbye
|
||||
> Ce-Specversion: 1.0
|
||||
> Ce-Type: greeting
|
||||
> Ce-Source: sendoff
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 41
|
||||
>
|
||||
< HTTP/1.1 202 Accepted
|
||||
< Date: Sun, 24 Jan 2021 23:04:15 GMT
|
||||
< Content-Length: 0
|
||||
```
|
||||
|
||||
**Output of hello-display (showing two events):**
|
||||
|
||||
|
||||
```
|
||||
$ kubectl -n eventing-test logs -l app=hello-display --tail=100
|
||||
☁️ cloudevents.Event
|
||||
@ -567,7 +535,6 @@ Data,
|
||||
|
||||
**Output of goodbye-display (also with two events):**
|
||||
|
||||
|
||||
```
|
||||
$ kubectl -n eventing-test logs -l app=goodbye-display --tail=100
|
||||
☁️ cloudevents.Event
|
||||
@ -611,15 +578,15 @@ Internal eventing in cloud events is pretty easy to track if it's going to a pre
|
||||
via: https://opensource.com/article/21/2/knative-eventing
|
||||
|
||||
作者:[Jessica Cherry][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][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/cherrybomb
|
||||
[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)
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/computer_space_graphic_cosmic.png
|
||||
[2]: https://opensource.com/article/20/11/knative
|
||||
[3]: https://opensource.com/resources/what-is-kubernetes
|
||||
[4]: https://en.wikipedia.org/wiki/Cloud_native_computing
|
||||
|
@ -1,15 +1,16 @@
|
||||
[#]: subject: (Use the Alpine email client in your Linux terminal)
|
||||
[#]: via: (https://opensource.com/article/21/5/alpine-linux-email)
|
||||
[#]: author: (David Both https://opensource.com/users/dboth)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: "Use the Alpine email client in your Linux terminal"
|
||||
[#]: via: "https://opensource.com/article/21/5/alpine-linux-email"
|
||||
[#]: author: "David Both https://opensource.com/users/dboth"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Use the Alpine email client in your Linux terminal
|
||||
======
|
||||
Configure Alpine to handle your email the way you like it.
|
||||
|
||||
![Chat via email][1]
|
||||
|
||||
Email is an important communications medium and will remain so for the foreseeable future. I have used many different email clients over the last 30 years, and [Thunderbird][2] is what I have used the most in recent years. It is an excellent and functional desktop application that provides all the features that most people need—including me.
|
||||
@ -22,7 +23,7 @@ This desire to go retro with my email client started back in 2017 when I wrote a
|
||||
|
||||
I recently decided to exclusively use Alpine for email. The main attraction is the ease of use offered by keeping my hands on the keyboard (and reducing the number of times I need to reach for the mouse). It is also about scratching my sysadmin itch to do something different and use an excellent text mode interface in the process.
|
||||
|
||||
## Getting started
|
||||
### Getting started
|
||||
|
||||
I already had Alpine set up from my previous use, so it was just a matter of starting to use it again.
|
||||
|
||||
@ -32,18 +33,17 @@ I previously set up Alpine on my mail server—I used secure shell (SSH) to log
|
||||
|
||||
But now I want to run Alpine on my workstation or laptop. It's relatively simple to configure Alpine on the same host as the email server. Using it on a remote computer requires a good bit more.
|
||||
|
||||
## Install Alpine
|
||||
### Install Alpine
|
||||
|
||||
Installing Alpine on Fedora is simple because it is available from the Fedora repository. Just use DNF as root:
|
||||
|
||||
|
||||
```
|
||||
`# dnf -y install alpine`
|
||||
# dnf -y install alpine
|
||||
```
|
||||
|
||||
This command installs Alpine and any prerequisite packages that are not already installed. Alpine's primary dependencies are Sendmail, Hunspell, OpenLDAP, OpenSSL, krb5-libs, ncurses, and a couple of others. In my case, Alpine was the only package installed.
|
||||
|
||||
## Launch Alpine
|
||||
### Launch Alpine
|
||||
|
||||
To launch Alpine, open a terminal session, type **alpine** on the command line, and press **Enter**.
|
||||
|
||||
@ -51,7 +51,6 @@ The first time you start Alpine, it displays a message that it is creating the u
|
||||
|
||||
For now, just press lowercase **e** to exit from the greeting message. You should now see Alpine's Main menu (I deleted several blank lines of the output to save space):
|
||||
|
||||
|
||||
```
|
||||
+----------------------------------------------------+
|
||||
| ALPINE 2.24 MAIN MENU Folder: INBOX No Messages |
|
||||
@ -77,25 +76,24 @@ For now, just press lowercase **e** to exit from the greeting message. You shoul
|
||||
| For Copyright information press "?" |
|
||||
| |
|
||||
| ? Help P PrevCmd R RelNotes |
|
||||
| O OTHER CMDS > [ListFldrs] N NextCmd K KBLock |
|
||||
| O OTHER CMDS > [ListFldrs] N NextCmd K KBLock |
|
||||
+----------------------------------------------------+
|
||||
```
|
||||
|
||||
_Figure 1: Alpine's Main menu_
|
||||
*Figure 1: Alpine's Main menu*
|
||||
|
||||
Alpine creates the `~mail` directory localhost during initial use. When you configure the IMAP server, Alpine creates the default `~/mail`, `~/mail/sent-mail`, and `saved-messages` folders in your home directory on the IMAP server. You can change the defaults, but I recommend against it. When using IMAP, emails are not stored locally unless you copy them to local folders. All emails are stored in the Inbox on the SMTP server until they are saved to a folder on the IMAP server. The SMTP and IMAP servers might use the same or different hosts.
|
||||
|
||||
Alpine also assumes that the Inbox is located at `/var/spool/mail/user_name` on the email SMTP server. This article explains how to configure both IMAP and SMTP servers. The email administrator for your organization—that might be you—will add your account to the IMAP server and provide you with the initial password.
|
||||
|
||||
## The Alpine interface
|
||||
### The Alpine interface
|
||||
|
||||
The Alpine user interface (UI) is a text-mode, menu-driven UI, also known as a TUI. This type of interface is also sometimes called captive user interface (CUI), which does not provide a command-line interface that can be used in scripts, for example. You must exit from the program to perform other tasks.
|
||||
|
||||
By contrast, the [mailx][5] program is an email program that can be used with either a TUI, from the command line, or in scripts. For example, you can use the following command to send the results of the free command directly to the sysadmin's email account:
|
||||
|
||||
|
||||
```
|
||||
`$ free | mailx -s "Free memory" sysadmin@example.com`
|
||||
$ free | mailx -s "Free memory" sysadmin@example.com
|
||||
```
|
||||
|
||||
But enough of that little side trip; there is work to do. Let's start with an explanation.
|
||||
@ -106,20 +104,19 @@ On the Main menu, you can use the **Up** and **Down** arrow keys to highlight a
|
||||
|
||||
Use the **Page Down** and **Page Up** keys to scroll through the commands if you can't see them all. The secondary menu at the bottom of the page usually lists all the commands available on the current menu; you will also see a message similar to this:
|
||||
|
||||
|
||||
```
|
||||
`[START of Information About Setup Command]`
|
||||
[START of Information About Setup Command]
|
||||
```
|
||||
|
||||
Should you find yourself at a place you don't want to be, such as creating a new email, responding to one, or making changes to settings, and decide you don't want to do that, **Ctrl+C** allows you to cancel the current task. In most cases, you will be asked to confirm that you want to cancel by pressing the **C** key. Note that **^C** in the secondary menu represents **Ctrl+C**. Many commands use the **Ctrl** key, so you will see **^** quite frequently on some menus.
|
||||
|
||||
Finally, to quit Alpine, you can press **Q**; when it asks, "Really quit Alpine?" respond with **Y** to exit. Like many commands, **Q** is not available from all menus.
|
||||
|
||||
## Help
|
||||
### Help
|
||||
|
||||
Help is available from all of the menus I have tried. You can access detailed help for each menu item by highlighting the item you need information for and pressing the **?** key to obtain context-sensitive help.
|
||||
|
||||
## Configuration
|
||||
### Configuration
|
||||
|
||||
When I started using Alpine regularly, I made the minimum changes to the configuration needed to send and receive emails. As I gained more experience with Alpine, I changed other configuration items to make things work easier or more to my liking.
|
||||
|
||||
@ -127,100 +124,97 @@ First, I will explain the basic configurations required to make Alpine work, the
|
||||
|
||||
If you have been exploring a bit on your own—which is a good thing—return to the Main menu. To get to Alpine's Configuration menu from the Main menu, type **S** for Setup. You will see a menu like this:
|
||||
|
||||
|
||||
```
|
||||
ALPINE 2.24 SETUP Folder: INBOX No Messages
|
||||
ALPINE 2.24 SETUP Folder: INBOX No Messages
|
||||
|
||||
This is the Setup screen for Alpine. Choose from the following commands:
|
||||
This is the Setup screen for Alpine. Choose from the following commands:
|
||||
|
||||
(E) Exit Setup:
|
||||
This puts you back at the Main Menu.
|
||||
(E) Exit Setup:
|
||||
This puts you back at the Main Menu.
|
||||
|
||||
(P) Printer:
|
||||
Allows you to set a default printer and to define custom
|
||||
print commands.
|
||||
(P) Printer:
|
||||
Allows you to set a default printer and to define custom
|
||||
print commands.
|
||||
|
||||
(N) Newpassword:
|
||||
Change your password.
|
||||
(N) Newpassword:
|
||||
Change your password.
|
||||
|
||||
(C) Config:
|
||||
Allows you to set or unset many features of Alpine.
|
||||
You may also set the values of many options with this command.
|
||||
(C) Config:
|
||||
Allows you to set or unset many features of Alpine.
|
||||
You may also set the values of many options with this command.
|
||||
|
||||
(S) Signature:
|
||||
Enter or edit a custom signature which will
|
||||
be included with each new message you send.
|
||||
(S) Signature:
|
||||
Enter or edit a custom signature which will
|
||||
be included with each new message you send.
|
||||
|
||||
(A) AddressBooks:
|
||||
Define a non-default address book.
|
||||
(A) AddressBooks:
|
||||
Define a non-default address book.
|
||||
|
||||
(L) collectionLists:
|
||||
You may define groups of folders to help you better organize your mail.
|
||||
(L) collectionLists:
|
||||
You may define groups of folders to help you better organize your mail.
|
||||
|
||||
(R) Rules:
|
||||
This has up to six sub-categories: Roles, Index Colors, Filters,
|
||||
[START of Information About Setup Command ]
|
||||
(R) Rules:
|
||||
This has up to six sub-categories: Roles, Index Colors, Filters,
|
||||
[START of Information About Setup Command ]
|
||||
? Help E Exit Setup N Newpassword S Signature L collectionList D Directory
|
||||
O OTHER CMDS P Printer C Config A AddressBooks R Rules K Kolor
|
||||
```
|
||||
|
||||
_Figure 2: Alpine Setup menu_
|
||||
*Figure 2: Alpine Setup menu*
|
||||
|
||||
The Setup menu groups the very large number of setup items into related categories to, hopefully, make the ones you want easier to locate. Use **Page Down** and **Page Up** to scroll through the commands if you can't see them all.
|
||||
|
||||
I'll start with the settings necessary to get email—Alpine's entire purpose—up and running.
|
||||
|
||||
## Config
|
||||
### Config
|
||||
|
||||
The Config section contains 15 pages (on my large screen) of option- and feature-configuration items. These settings can be used to set up your SMTP and IMAP connections to the email server and define the way many aspects of Alpine work. In these examples, I'll use the `example.com` domain name (which is the virtual network I use for testing and experimenting). Alpine's configuration is stored in the `~/.pinerc` file, created the first time you start Alpine.
|
||||
|
||||
The first page of the Setup Configuration menu contains most of the settings required to configure Alpine to send and receive email:
|
||||
|
||||
|
||||
```
|
||||
ALPINE 2.24 SETUP CONFIGURATION Folder: INBOX No Messages
|
||||
|
||||
Personal Name = <No Value Set: using "Test User">
|
||||
User Domain = <No Value Set>
|
||||
SMTP Server (for sending) = <No Value Set>
|
||||
NNTP Server (for news) = <No Value Set>
|
||||
Inbox Path = <No Value Set: using "inbox">
|
||||
Incoming Archive Folders = <No Value Set>
|
||||
Pruned Folders = <No Value Set>
|
||||
Default Fcc (File carbon copy) = <No Value Set: using "sent-mail">
|
||||
Default Saved Message Folder = <No Value Set: using "saved-messages">
|
||||
Postponed Folder = <No Value Set: using "postponed-msgs">
|
||||
Read Message Folder = <No Value Set>
|
||||
Form Letter Folder = <No Value Set>
|
||||
Trash Folder = <No Value Set: using "Trash">
|
||||
Literal Signature = <No Value Set>
|
||||
Signature File = <No Value Set: using ".signature">
|
||||
Personal Name = <No Value Set: using "Test User">
|
||||
User Domain = <No Value Set>
|
||||
SMTP Server (for sending) = <No Value Set>
|
||||
NNTP Server (for news) = <No Value Set>
|
||||
Inbox Path = <No Value Set: using "inbox">
|
||||
Incoming Archive Folders = <No Value Set>
|
||||
Pruned Folders = <No Value Set>
|
||||
Default Fcc (File carbon copy) = <No Value Set: using "sent-mail">
|
||||
Default Saved Message Folder = <No Value Set: using "saved-messages">
|
||||
Postponed Folder = <No Value Set: using "postponed-msgs">
|
||||
Read Message Folder = <No Value Set>
|
||||
Form Letter Folder = <No Value Set>
|
||||
Trash Folder = <No Value Set: using "Trash">
|
||||
Literal Signature = <No Value Set>
|
||||
Signature File = <No Value Set: using ".signature">
|
||||
Feature List =
|
||||
Set Feature Name
|
||||
\--- ----------------------
|
||||
--- ----------------------
|
||||
[ Composer Preferences ]
|
||||
[X] Allow Changing From (default)
|
||||
[ ] Alternate Compose Menu
|
||||
[ ] Alternate Role (#) Menu
|
||||
[ ] Compose Cancel Confirm Uses Yes
|
||||
[ ] Compose Rejects Unqualified Addresses
|
||||
[ ] Compose Send Offers First Filter
|
||||
[ ] Ctrl-K Cuts From Cursor
|
||||
[ ] Delete Key Maps to Ctrl-D
|
||||
[ ] Do Not Save to Deadletter on Cancel
|
||||
[ ] Alternate Compose Menu
|
||||
[ ] Alternate Role (#) Menu
|
||||
[ ] Compose Cancel Confirm Uses Yes
|
||||
[ ] Compose Rejects Unqualified Addresses
|
||||
[ ] Compose Send Offers First Filter
|
||||
[ ] Ctrl-K Cuts From Cursor
|
||||
[ ] Delete Key Maps to Ctrl-D
|
||||
[ ] Do Not Save to Deadletter on Cancel
|
||||
[Already at start of screen]
|
||||
? Help E Exit Setup P Prev - PrevPage A Add Value % Print
|
||||
? Help E Exit Setup P Prev - PrevPage A Add Value % Print
|
||||
O OTHER CMDS C [Change Val] N Next Spc NextPage D Delete Val W WhereIs
|
||||
```
|
||||
|
||||
_Figure 3: First page of Alpine's Setup Configuration menu_
|
||||
*Figure 3: First page of Alpine's Setup Configuration menu*
|
||||
|
||||
This is where you define the parameters required to communicate with the email server. To change a setting, use the **Arrow** keys to move the selection bar to the desired configuration item and press **Enter**. You can see in Figure 3 that none of the basic configuration items have any values set.
|
||||
|
||||
The **Personal Name** item uses the [Gecos field][6] of the Unix `/etc/passwd` entry for the logged-in user to obtain the default name. This is just a name Alpine uses for display and has no role in receiving or sending email. I usually call this the "pretty name." In this case, the default name is fine, so I will leave it as it is.
|
||||
|
||||
There are some configuration items that you must set. Start with the **User Domain**, which is the current computer's domain name. Mine is a virtual machine I use for testing and examples in my books. Use the command line to get the fully qualified domain name (FQDN) and the hostname. In Figure 4, you can see that the domain name is `example.com`:
|
||||
|
||||
There are some configuration items that you must set. Start with the **User Domain**, which is the current computer's domain name. Mine is a virtual machine I use for testing and examples in my books. Use the command line to get the fully qualified domain name (FQDN) and the hostname. In Figure 4, you can see that the domain name is `example.com` :
|
||||
|
||||
```
|
||||
$ hostnamectl
|
||||
@ -236,117 +230,161 @@ Kernel: Linux 5.10.23-200.fc33.x86_64
|
||||
Architecture: x86-64
|
||||
```
|
||||
|
||||
_Figure 4: Obtaining the hostname and domain name_
|
||||
|
||||
Once you have the FQDN, select the **User Domain** entry and press **Enter** to see the entry field at the bottom of the Alpine screen (as shown in Figure 5). Type your domain name and press **Enter** (using _your_ network's domain and server names):
|
||||
*Figure 4: Obtaining the hostname and domain name*
|
||||
|
||||
Once you have the FQDN, select the **User Domain** entry and press **Enter** to see the entry field at the bottom of the Alpine screen (as shown in Figure 5). Type your domain name and press **Enter** (using *your* network's domain and server names):
|
||||
|
||||
```
|
||||
ALPINE 2.24 SETUP CONFIGURATION Folder: INBOX No Messages
|
||||
|
||||
Personal Name = <No Value Set: using "Test User">
|
||||
User Domain = <No Value Set>
|
||||
SMTP Server (for sending) = <No Value Set>
|
||||
NNTP Server (for news) = <No Value Set>
|
||||
Inbox Path = <No Value Set: using "inbox">
|
||||
Incoming Archive Folders = <No Value Set>
|
||||
Pruned Folders = <No Value Set>
|
||||
Default Fcc (File carbon copy) = <No Value Set: using "sent-mail">
|
||||
Default Saved Message Folder = <No Value Set: using "saved-messages">
|
||||
Postponed Folder = <No Value Set: using "postponed-msgs">
|
||||
Read Message Folder = <No Value Set>
|
||||
Form Letter Folder = <No Value Set>
|
||||
Trash Folder = <No Value Set: using "Trash">
|
||||
Literal Signature = <No Value Set>
|
||||
Signature File = <No Value Set: using ".signature">
|
||||
Personal Name = <No Value Set: using "Test User">
|
||||
User Domain = <No Value Set>
|
||||
SMTP Server (for sending) = <No Value Set>
|
||||
NNTP Server (for news) = <No Value Set>
|
||||
Inbox Path = <No Value Set: using "inbox">
|
||||
Incoming Archive Folders = <No Value Set>
|
||||
Pruned Folders = <No Value Set>
|
||||
Default Fcc (File carbon copy) = <No Value Set: using "sent-mail">
|
||||
Default Saved Message Folder = <No Value Set: using "saved-messages">
|
||||
Postponed Folder = <No Value Set: using "postponed-msgs">
|
||||
Read Message Folder = <No Value Set>
|
||||
Form Letter Folder = <No Value Set>
|
||||
Trash Folder = <No Value Set: using "Trash">
|
||||
Literal Signature = <No Value Set>
|
||||
Signature File = <No Value Set: using ".signature">
|
||||
Feature List =
|
||||
Set Feature Name
|
||||
\--- ----------------------
|
||||
--- ----------------------
|
||||
[ Composer Preferences ]
|
||||
[X] Allow Changing From (default)
|
||||
[ ] Alternate Compose Menu
|
||||
[ ] Alternate Role (#) Menu
|
||||
[ ] Compose Cancel Confirm Uses Yes
|
||||
[ ] Compose Rejects Unqualified Addresses
|
||||
[ ] Compose Send Offers First Filter
|
||||
[ ] Ctrl-K Cuts From Cursor
|
||||
[ ] Delete Key Maps to Ctrl-D
|
||||
[ ] Do Not Save to Deadletter on Cancel
|
||||
Enter the text to be added : example.com
|
||||
^G Help
|
||||
[ ] Alternate Compose Menu
|
||||
[ ] Alternate Role (#) Menu
|
||||
[ ] Compose Cancel Confirm Uses Yes
|
||||
[ ] Compose Rejects Unqualified Addresses
|
||||
[ ] Compose Send Offers First Filter
|
||||
[ ] Ctrl-K Cuts From Cursor
|
||||
[ ] Delete Key Maps to Ctrl-D
|
||||
[ ] Do Not Save to Deadletter on Cancel
|
||||
Enter the text to be added : example.com
|
||||
^G Help
|
||||
^C Cancel Ret Accept
|
||||
```
|
||||
|
||||
_Figure 5: Type the domain name into the text entry field._
|
||||
*Figure 5: Type the domain name into the text entry field.*
|
||||
|
||||
### Required config
|
||||
#### Required config
|
||||
|
||||
These are the basic configuration items you need to send and receive email:
|
||||
|
||||
* **Personal Name**
|
||||
* Your name
|
||||
* This is the pretty name Alpine uses for the From and Return fields in emails.
|
||||
* **User Domain**
|
||||
* `example.com:25/user=SMTP_Authentication_UserName`
|
||||
* This is the email domain for your email client. This might be different from the User Domain name. This line also contains the SMTP port number and the user name for SMTP authentication.
|
||||
* **SMTP server**
|
||||
* SMTP
|
||||
* This is the name of the outbound SMTP email server. It combines with the User Domain name to create the FQDN for the email server.
|
||||
* **Inbox Path**
|
||||
* `{IMAP_server)}Inbox`
|
||||
* This is the name of the IMAP server enclosed in curly braces (`{}`) and the name of the Inbox. Note that this directory location is different from the inbound IMAP email. The usual location for the inbox on the server is `/var/spool/mail/user_name`.
|
||||
* **Default Fcc** (file carbon copy)
|
||||
* `{IMAP_server)}mail/sent`
|
||||
* This is the mailbox (folder) where sent mail is stored. The default mail directory on the server is usually `~/mail`, but `mail/` must be specified in this and the next two entries, or the folders will be placed in the home directory instead.
|
||||
* **Default Saved Message Folder**
|
||||
* `{IMAP_server)}mail/saved-messages`
|
||||
* This is the default folder when saving a message to a folder if you don't use `^t` to specify a different one.
|
||||
* **Trash Folder**
|
||||
* `{IMAP_server)}mail/Trash`
|
||||
* **Literal Signature**
|
||||
* A signature string
|
||||
* I don't use this, but it's an easy place to specify a simple signature.
|
||||
* **Signature File**
|
||||
* `~/MySignature.sig`
|
||||
* This points to the file that contains your signature file.
|
||||
* Personal Name
|
||||
* Your name
|
||||
* This is the pretty name Alpine uses for the From and Return fields in emails.
|
||||
* User Domain
|
||||
* example.com:25/user=SMTP_Authentication_UserName
|
||||
* This is the email domain for your email client. This might be different from the User Domain name. This line also contains the SMTP port number and the user name for SMTP authentication.
|
||||
* * SMTP server
|
||||
SMTP
|
||||
* This is the name of the outbound SMTP email server. It combines with the User Domain name to create the FQDN for the email server.
|
||||
* Inbox Path
|
||||
* {IMAP_server)}Inbox
|
||||
* This is the name of the IMAP server enclosed in curly braces ({}) and the name of the Inbox. Note that this directory location is different from the inbound IMAP email. The usual location for the inbox on the server is `/var/spool/mail/user_name`.
|
||||
* Default Fcc (file carbon copy)
|
||||
* {IMAP_server)}mail/sent
|
||||
* This is the mailbox (folder) where sent mail is stored. The default mail directory on the server is usually `~/mail`, but `mail/` must be specified in this and the next two entries, or the folders will be placed in the home directory instead.
|
||||
* Default Saved Message Folder
|
||||
* {IMAP_server)}mail/saved-messages
|
||||
* This is the default folder when saving a message to a folder if you don't use `^t` to specify a different one.
|
||||
* Trash Folder
|
||||
* {IMAP_server)}mail/Trash
|
||||
* Literal Signature
|
||||
* A signature string
|
||||
* I don't use this, but it's an easy place to specify a simple signature.
|
||||
* Signature File
|
||||
* ~/MySignature.sig
|
||||
* This points to the file that contains your signature file.
|
||||
|
||||
|
||||
|
||||
### Optional config
|
||||
#### Optional config
|
||||
|
||||
Here are the features I changed to make Alpine work more to my liking. They are not about getting Alpine to send and receive email, but about making Alpine work the way you want it to. Unless otherwise noted, I turned all of these features on. Features that are turned on by default have the string `(default)` next to them in the Alpine display. Because they are already turned on, I will not describe them.
|
||||
|
||||
* **Alternate Role (`#`) Menu:** This allows multiple identities using different email addresses on the same client and server. The server must be configured to allow multiple addresses to be delivered to your primary email account.
|
||||
* **Compose Rejects Unqualified Addresses:** Alpine will not accept an address that is not fully qualified. That is, it must be in the form `<username@example.com>`.
|
||||
* **Enable Sigdashes:** This enables Alpine to automatically add dashes (`--`) in the row just above the signature. This is a common way of delineating the start of the signature.
|
||||
* **Prevent User Lookup in Password File:** This prevents the lookup of the full user name from the Gecos field of the passwd file.
|
||||
* **Spell Check Before Sending:** Although you can invoke the spell checker at any time while composing an email, this forces a spell check when you use the `^X` keystroke to send an email.
|
||||
* **Include Header in Reply:** This includes a message's headers when you reply.
|
||||
* **Include Text in Reply:** This includes the text of the original message in your reply.
|
||||
* **Signature at Bottom:** Many people prefer to have their signature at the very bottom of the email. This setting changes the default, which puts the signature at the end of the reply and before the message being replied to.
|
||||
* **Preserve Original Fields:** This preserves the original addresses in the **To:** and **CC:** fields when you reply to a message. If this feature is disabled when you reply to a message, the original sender is added to the **To:** field, all other recipients are added to the **CC:** field, and your address is added to the **From:** field.
|
||||
* **Enable Background Sending:** This speeds the Alpine user interface response when sending an email.
|
||||
* **Enable Verbose SMTP Posting:** This produces more verbose information during SMTP conversations with the server. It is a problem-determination aid for the sysadmin.
|
||||
* **Warn if Blank Subject:** This prevents sending emails with no subject.
|
||||
* **Combined Folder Display:** This combines all folder collections into a single main display. Otherwise, collections will be in separate views.
|
||||
* **Combined Subdirectory Display:** This combines all subdirectories' collections into a single main display. Otherwise, subdirectories will be in separate views. This is useful when searching for a subdirectory to attach or save files.
|
||||
* **Enable Incoming Folders Collection:** This lists all incoming folders in the same collection as the Inbox. Incoming folders can be used with a tool like procmail to presort email into folders other than the Inbox and makes it easier to see the folders where new emails are sorted.
|
||||
* **Enable Incoming Folders Checking:** This enables Alpine to check for new emails in the incoming folders collection.
|
||||
* **Incoming Checking Includes Total:** This displays the number of old and new emails in the incoming folders.
|
||||
* **Expanded View of Folders:** This displays all folders in each collection when you view the **Folder List** screen. Otherwise, only the collections are shown, and the folders are not shown until selected.
|
||||
* **Separate Folder and Directory Entries:** If your mail directory has email folders and regular directories that use the same name, this causes Alpine to list them separately.
|
||||
* **Use Vertical Folder List:** This sorts mail folders vertically first and then horizontally. The default is horizontal, then vertical.
|
||||
* **Convert Dates To Localtime:** By default, all dates and times are displayed in their originating time zones. This converts the dates to display in local time.
|
||||
* **Show Sort in Titlebar:** Alpine can sort emails in a mail folder using multiple criteria. This causes the sort criteria to be displayed in the title bar.
|
||||
* **Enable Message View Address Links:** This highlights email addresses in the body of the email.
|
||||
* **Enable Message View Attachment Links:** This highlights URL links in the body of the email.
|
||||
* **Prefer Plain Text:** Many emails contain two versions, plain text and HTML. When this feature is turned on, Alpine always displays the plain text version. You can use the **A** key to toggle to the "preferred" version, usually the HTML one. I usually find the plain text easier to visualize the structure of and read the email. This can depend upon the sending client, so I use the **A** key when needed.
|
||||
* **Enable Print Via Y Command:** This prints a message using the previous default, **Y**. Because **Y** is also used to confirm many commands, the keystroke can inadvertently cause you to print a message. The new default is **%** to prevent accidental printing. I like the ease of using **Y**, but it has caused some extra print jobs, so I am thinking about turning this feature off.
|
||||
* **Print Formfeed Between Messages:** This prints each message on a new sheet of paper.
|
||||
* **Customized Headers:** Customized headers enables overriding the default **From:** and **Reply-To:** headers. I set mine to: [code] - From: "David Both" <[[david@example.com][7]](mailto:[david@both.org][8])>
|
||||
\- Reply-To: "David Both"
|
||||
<[[david@example.com][7]](mailto:[david@both.org][8])>
|
||||
* Alternate Role (#) Menu: This allows multiple identities using different email addresses on the same client and server. The server must be configured to allow multiple addresses to be delivered to your primary email account.
|
||||
* Compose Rejects Unqualified Addresses: Alpine will not accept an address that is not fully qualified. That is, it must be in the form `<username@example.com>`.
|
||||
* Enable Sigdashes: This enables Alpine to automatically add dashes (--) in the row just above the signature. This is a common way of delineating the start of the signature.
|
||||
* Prevent User Lookup in Password File: This prevents the lookup of the full user name from the Gecos field of the passwd file.
|
||||
* Spell Check Before Sending: Although you can invoke the spell checker at any time while composing an email, this forces a spell check when you use the `^X` keystroke to send an email.
|
||||
* Include Header in Reply: This includes a message's headers when you reply.
|
||||
* Include Text in Reply: This includes the text of the original message in your reply.
|
||||
* Signature at Bottom: Many people prefer to have their signature at the very bottom of the email. This setting changes the default, which puts the signature at the end of the reply and before the message being replied to.
|
||||
* Preserve Original Fields: This preserves the original addresses in the To: and CC: fields when you reply to a message. If this feature is disabled when you reply to a message, the original sender is added to the To: field, all other recipients are added to the CC: field, and your address is added to the From: field.
|
||||
* Enable Background Sending: This speeds the Alpine user interface response when sending an email.
|
||||
* Enable Verbose SMTP Posting: This produces more verbose information during SMTP conversations with the server. It is a problem-determination aid for the sysadmin.
|
||||
* Warn if Blank Subject: This prevents sending emails with no subject.
|
||||
* Combined Folder Display: This combines all folder collections into a single main display. Otherwise, collections will be in separate views.
|
||||
* Combined Subdirectory Display: This combines all subdirectories' collections into a single main display. Otherwise, subdirectories will be in separate views. This is useful when searching for a subdirectory to attach or save files.
|
||||
* Enable Incoming Folders Collection: This lists all incoming folders in the same collection as the Inbox. Incoming folders can be used with a tool like procmail to presort email into folders other than the Inbox and makes it easier to see the folders where new emails are sorted.
|
||||
* Enable Incoming Folders Checking: This enables Alpine to check for new emails in the incoming folders collection.
|
||||
* Incoming Checking Includes Total: This displays the number of old and new emails in the incoming folders.
|
||||
* Expanded View of Folders: This displays all folders in each collection when you view the Folder List screen. Otherwise, only the collections are shown, and the folders are not shown until selected.
|
||||
* Separate Folder and Directory Entries: If your mail directory has email folders and regular directories that use the same name, this causes Alpine to list them separately.
|
||||
* Use Vertical Folder List: This sorts mail folders vertically first and then horizontally. The default is horizontal, then vertical.
|
||||
* Convert Dates To Localtime: By default, all dates and times are displayed in their originating time zones. This converts the dates to display in local time.
|
||||
* Show Sort in Titlebar: Alpine can sort emails in a mail folder using multiple criteria. This causes the sort criteria to be displayed in the title bar.
|
||||
* Enable Message View Address Links: This highlights email addresses in the body of the email.
|
||||
* Enable Message View Attachment Links: This highlights URL links in the body of the email.
|
||||
* Prefer Plain Text: Many emails contain two versions, plain text and HTML. When this feature is turned on, Alpine always displays the plain text version. You can use the A key to toggle to the "preferred" version, usually the HTML one. I usually find the plain text easier to visualize the structure of and read the email. This can depend upon the sending client, so I use the A key when needed.
|
||||
* Enable Print Via Y Command: This prints a message using the previous default, Y. Because Y is also used to confirm many commands, the keystroke can inadvertently cause you to print a message. The new default is % to prevent accidental printing. I like the ease of using Y, but it has caused some extra print jobs, so I am thinking about turning this feature off.
|
||||
* Print Formfeed Between Messages: This prints each message on a new sheet of paper.
|
||||
* Customized Headers: Customized headers enables overriding the default From: and Reply-To: headers. I set mine to:
|
||||
- From: "David Both" <[david@example.com](mailto:david@both.org)>
|
||||
- Reply-To: "David Both"
|
||||
<[david@example.com](mailto:david@both.org)>
|
||||
* Sort key: By default, Alpine sorts messages in a folder by arrival time. I found this to be a bit confusing, so I changed it to Date, which can be significantly different from arrival time. Many spammers use dates and times in the past or future, so this setting can sort the future ones to the top of the list (or bottom, depending on your preferences for forward or reverse sorts).
|
||||
* Image Viewer: This feature allows you to specify the image viewer to use when displaying graphics attached to or embedded in an email. This only works when using Alpine in a terminal window on the graphical desktop. It will not work in a text-only virtual console. I always set this to `=okular` because [Okular][7] is my preferred viewer.
|
||||
* URL-Viewer: This tells Alpine what web browser you want to use. I set this for `= /bin/firefox` but you could use Chrome or another browser. Be sure to verify the location of the Firefox executable.
|
||||
|
||||
#### Printing
|
||||
|
||||
It is easy to set up Alpine for printing. Select the **Printer** menu from the **Setup** page. This allows you to set a default printer and define custom print commands. The default is probably `attached-to-ansi`. Move the cursor down to the **Standard UNIX print command** section and highlight the printer list.
|
||||
|
||||
```
|
||||
* **Sort key:** By default, Alpine sorts messages in a folder by arrival time. I found this to be a bit confusing, so I changed it to **Date**, which can be significantly different from arrival time. Many spammers use dates and times in the past or future, so this setting can sort the future ones to the top of the list (or bottom, depending on your preferences for forward or reverse sorts).
|
||||
* **Image Viewer:** This feature allows you to specify the image viewer to use when displaying graphics attached to or embedded in an email. This only works when using Alpine in a terminal window on the graphical desktop. It will not work in a text-only virtual console. I always set this to `=okular` because [Okular][9] is my preferred viewer.
|
||||
* **URL-Viewer:** This tells Alpine what web browser you
|
||||
Standard UNIX print command
|
||||
|
||||
Using this option may require setting your "PRINTER" or "LPDEST"
|
||||
|
||||
environment variable using the standard UNIX utilities.
|
||||
|
||||
Printer List: "" lpr
|
||||
```
|
||||
|
||||
Then press the **Enter** key to set the standard Unix **lpr** command as the default.
|
||||
|
||||
### Final thoughts
|
||||
|
||||
This is not a step-by-step guide to Alpine configuration and use. Rather, I tried to cover the basics to get it up and running to send and receive email. I also shared some configuration changes that have made my Alpine experience much more usable. These are the configuration items that I've found most important to my experience; you may find that others are more important to you.
|
||||
|
||||
I have been using Alpine for several months now and am very happy with the experience. The text interface helps me concentrate on the message and not the distracting graphics and animations. I can view those if I choose, but 99% of the time, I choose not to.
|
||||
|
||||
Alpine is easy to use and has a huge number of features that can be configured to give the best email client experience possible.
|
||||
|
||||
Use the **Help** feature to get more information about the fields I explored above and those that I did not cover. You will undoubtedly find ways to configure Alpine that work better for you than the defaults or what I changed. I hope this will at least give you a start to set up Alpine the way you want.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/5/alpine-linux-email
|
||||
|
||||
作者:[David Both][a]
|
||||
选题:[lkxed][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/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/email_chat_communication_message.png
|
||||
[2]: https://www.thunderbird.net/en-US/
|
||||
[3]: https://alpine.x10host.com/
|
||||
[4]: https://opensource.com/article/17/10/alpine-email-client
|
||||
[5]: https://linux.die.net/man/1/mailx
|
||||
[6]: https://en.wikipedia.org/wiki/Gecos_field
|
||||
[7]: https://okular.kde.org/
|
||||
|
@ -1,15 +1,16 @@
|
||||
[#]: subject: (4 steps to set up global modals in React)
|
||||
[#]: via: (https://opensource.com/article/21/5/global-modals-react)
|
||||
[#]: author: (Ajay Pratap https://opensource.com/users/ajaypratap)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: "4 steps to set up global modals in React"
|
||||
[#]: via: "https://opensource.com/article/21/5/global-modals-react"
|
||||
[#]: author: "Ajay Pratap https://opensource.com/users/ajaypratap"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
4 steps to set up global modals in React
|
||||
======
|
||||
Learn how to create interactive pop-up windows in a React web app.
|
||||
|
||||
![Digital creative of a browser on the internet][1]
|
||||
|
||||
A modal dialog is a window that appears on top of a web page and requires a user's interaction before it disappears. [React][2] has a couple of ways to help you generate and manage modals with minimal coding.
|
||||
@ -24,11 +25,10 @@ In my opinion, the best way to manage modal dialogs in your React application is
|
||||
|
||||
Here are the steps (and code) to set up global modals in React. I'm using [Patternfly][3] as my foundation, but the principles apply to any project.
|
||||
|
||||
#### 1\. Create a global modal component
|
||||
#### 1. Create a global modal component
|
||||
|
||||
In a file called **GlobalModal.tsx**, create your modal definition:
|
||||
|
||||
|
||||
```
|
||||
import React, { useState, createContext, useContext } from 'react';
|
||||
import { CreateModal, DeleteModal,UpdateModal } from './components';
|
||||
@ -46,25 +46,25 @@ const MODAL_COMPONENTS: any = {
|
||||
};
|
||||
|
||||
type GlobalModalContext = {
|
||||
showModal: (modalType: string, modalProps?: any) => void;
|
||||
hideModal: () => void;
|
||||
showModal: (modalType: string, modalProps?: any) => void;
|
||||
hideModal: () => void;
|
||||
store: any;
|
||||
};
|
||||
|
||||
const initalState: GlobalModalContext = {
|
||||
showModal: () => {},
|
||||
hideModal: () => {},
|
||||
showModal: () => {},
|
||||
hideModal: () => {},
|
||||
store: {},
|
||||
};
|
||||
|
||||
const GlobalModalContext = createContext(initalState);
|
||||
export const useGlobalModalContext = () => useContext(GlobalModalContext);
|
||||
export const useGlobalModalContext = () => useContext(GlobalModalContext);
|
||||
|
||||
export const GlobalModal: React.FC<{}> = ({ children }) => {
|
||||
export const GlobalModal: React.FC<{}> = ({ children }) => {
|
||||
const [store, setStore] = useState();
|
||||
const { modalType, modalProps } = store || {};
|
||||
|
||||
const showModal = (modalType: string, modalProps: any = {}) => {
|
||||
const showModal = (modalType: string, modalProps: any = {}) => {
|
||||
setStore({
|
||||
...store,
|
||||
modalType,
|
||||
@ -72,7 +72,7 @@ export const GlobalModal: React.FC<{}> = ({ children }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const hideModal = () => {
|
||||
const hideModal = () => {
|
||||
setStore({
|
||||
...store,
|
||||
modalType: null,
|
||||
@ -80,19 +80,19 @@ export const GlobalModal: React.FC<{}> = ({ children }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const renderComponent = () => {
|
||||
const renderComponent = () => {
|
||||
const ModalComponent = MODAL_COMPONENTS[modalType];
|
||||
if (!modalType || !ModalComponent) {
|
||||
return null;
|
||||
}
|
||||
return <ModalComponent id="global-modal" {...modalProps} />;
|
||||
return <ModalComponent id="global-modal" {...modalProps} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<GlobalModalContext.Provider value={{ store, showModal, hideModal }}>
|
||||
<GlobalModalContext.Provider value={{ store, showModal, hideModal }}>
|
||||
{renderComponent()}
|
||||
{children}
|
||||
</GlobalModalContext.Provider>
|
||||
</GlobalModalContext.Provider>
|
||||
);
|
||||
};
|
||||
```
|
||||
@ -103,40 +103,39 @@ The `showModal` function takes two parameters: `modalType` and `modalProps`. The
|
||||
|
||||
The `hideModal` function doesn't have any parameters; calling it causes the current open modal to close.
|
||||
|
||||
#### 2\. Create modal dialog components
|
||||
#### 2. Create modal dialog components
|
||||
|
||||
In a file called **CreateModal.tsx**, create a modal:
|
||||
|
||||
|
||||
```
|
||||
import React from "react";
|
||||
import { Modal, ModalVariant, Button } from "@patternfly/react-core";
|
||||
import { useGlobalModalContext } from "../GlobalModal";
|
||||
|
||||
export const CreateModal = () => {
|
||||
export const CreateModal = () => {
|
||||
const { hideModal, store } = useGlobalModalContext();
|
||||
const { modalProps } = store || {};
|
||||
const { title, confirmBtn } = modalProps || {};
|
||||
|
||||
const handleModalToggle = () => {
|
||||
const handleModalToggle = () => {
|
||||
hideModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
<Modal
|
||||
variant={ModalVariant.medium}
|
||||
title={title || "Create Modal"}
|
||||
isOpen={true}
|
||||
onClose={handleModalToggle}
|
||||
actions={[
|
||||
<Button key="confirm" variant="primary" onClick={handleModalToggle}>
|
||||
<Button key="confirm" variant="primary" onClick={handleModalToggle}>
|
||||
{confirmBtn || "Confirm button"}
|
||||
</Button>,
|
||||
<Button key="cancel" variant="link" onClick={handleModalToggle}>
|
||||
</Button>,
|
||||
<Button key="cancel" variant="link" onClick={handleModalToggle}>
|
||||
Cancel
|
||||
</Button>
|
||||
</Button>
|
||||
]}
|
||||
>
|
||||
>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
||||
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
||||
@ -144,7 +143,7 @@ export const CreateModal = () => {
|
||||
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
|
||||
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
|
||||
est laborum.
|
||||
</Modal>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
```
|
||||
@ -153,34 +152,33 @@ This has a custom hook, `useGlobalModalContext`, that provides store object from
|
||||
|
||||
To delete a modal, create a file called **DeleteModal.tsx**:
|
||||
|
||||
|
||||
```
|
||||
import React from "react";
|
||||
import { Modal, ModalVariant, Button } from "@patternfly/react-core";
|
||||
import { useGlobalModalContext } from "../GlobalModal";
|
||||
|
||||
export const DeleteModal = () => {
|
||||
export const DeleteModal = () => {
|
||||
const { hideModal } = useGlobalModalContext();
|
||||
|
||||
const handleModalToggle = () => {
|
||||
const handleModalToggle = () => {
|
||||
hideModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
<Modal
|
||||
variant={ModalVariant.medium}
|
||||
title="Delete Modal"
|
||||
isOpen={true}
|
||||
onClose={handleModalToggle}
|
||||
actions={[
|
||||
<Button key="confirm" variant="primary" onClick={handleModalToggle}>
|
||||
<Button key="confirm" variant="primary" onClick={handleModalToggle}>
|
||||
Confirm
|
||||
</Button>,
|
||||
<Button key="cancel" variant="link" onClick={handleModalToggle}>
|
||||
</Button>,
|
||||
<Button key="cancel" variant="link" onClick={handleModalToggle}>
|
||||
Cancel
|
||||
</Button>
|
||||
</Button>
|
||||
]}
|
||||
>
|
||||
>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
||||
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
||||
@ -188,41 +186,40 @@ export const DeleteModal = () => {
|
||||
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
|
||||
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
|
||||
est laborum.
|
||||
</Modal>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
To update a modal, create a file called **UpdateModal.tsx** and add this code:
|
||||
|
||||
|
||||
```
|
||||
import React from "react";
|
||||
import { Modal, ModalVariant, Button } from "@patternfly/react-core";
|
||||
import { useGlobalModalContext } from "../GlobalModal";
|
||||
|
||||
export const UpdateModal = () => {
|
||||
export const UpdateModal = () => {
|
||||
const { hideModal } = useGlobalModalContext();
|
||||
|
||||
const handleModalToggle = () => {
|
||||
const handleModalToggle = () => {
|
||||
hideModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
<Modal
|
||||
variant={ModalVariant.medium}
|
||||
title="Update Modal"
|
||||
isOpen={true}
|
||||
onClose={handleModalToggle}
|
||||
actions={[
|
||||
<Button key="confirm" variant="primary" onClick={handleModalToggle}>
|
||||
<Button key="confirm" variant="primary" onClick={handleModalToggle}>
|
||||
Confirm
|
||||
</Button>,
|
||||
<Button key="cancel" variant="link" onClick={handleModalToggle}>
|
||||
</Button>,
|
||||
<Button key="cancel" variant="link" onClick={handleModalToggle}>
|
||||
Cancel
|
||||
</Button>
|
||||
</Button>
|
||||
]}
|
||||
>
|
||||
>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
||||
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
||||
@ -230,15 +227,14 @@ export const UpdateModal = () => {
|
||||
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
|
||||
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
|
||||
est laborum.
|
||||
</Modal>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
#### 3\. Integrate GlobalModal into the top-level component in your application
|
||||
|
||||
To integrate the new modal structure you've created into your app, you just import the global modal class you've created. Here's my sample **App.tsx** file:
|
||||
#### 3. Integrate GlobalModal into the top-level component in your application
|
||||
|
||||
To integrate the new modal structure you've created into your app, you just import the global modal class you've created. Here's my sample **App.tsx**file:
|
||||
|
||||
```
|
||||
import "@patternfly/react-core/dist/styles/base.css";
|
||||
@ -248,9 +244,9 @@ import { AppLayout } from "./AppLayout";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<GlobalModal>
|
||||
<AppLayout />
|
||||
</GlobalModal>
|
||||
<GlobalModal>
|
||||
<AppLayout />
|
||||
</GlobalModal>
|
||||
);
|
||||
}
|
||||
```
|
||||
@ -259,55 +255,54 @@ App.tsx is the top-level component in your app, but you can add another componen
|
||||
|
||||
`GlobalModal` is the root-level component where all your modal components are imported and mapped with their specific `modalType`.
|
||||
|
||||
#### 4\. Select the modal's button from the AppLayout component
|
||||
#### 4. Select the modal's button from the AppLayout component
|
||||
|
||||
Adding a button to your modal with **AppLayout.js**:
|
||||
|
||||
|
||||
```
|
||||
import React from "react";
|
||||
import { Button, ButtonVariant } from "@patternfly/react-core";
|
||||
import { useGlobalModalContext, MODAL_TYPES } from "./components/GlobalModal";
|
||||
|
||||
export const AppLayout = () => {
|
||||
export const AppLayout = () => {
|
||||
const { showModal } = useGlobalModalContext();
|
||||
|
||||
const createModal = () => {
|
||||
const createModal = () => {
|
||||
showModal(MODAL_TYPES.CREATE_MODAL, {
|
||||
title: "Create instance form",
|
||||
confirmBtn: "Save"
|
||||
});
|
||||
};
|
||||
|
||||
const deleteModal = () => {
|
||||
const deleteModal = () => {
|
||||
showModal(MODAL_TYPES.DELETE_MODAL);
|
||||
};
|
||||
|
||||
const updateModal = () => {
|
||||
const updateModal = () => {
|
||||
showModal(MODAL_TYPES.UPDATE_MODAL);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button variant={ButtonVariant.primary} onClick={createModal}>
|
||||
<>
|
||||
<Button variant={ButtonVariant.primary} onClick={createModal}>
|
||||
Create Modal
|
||||
</Button>
|
||||
<br />
|
||||
<br />
|
||||
<Button variant={ButtonVariant.primary} onClick={deleteModal}>
|
||||
</Button>
|
||||
<br />
|
||||
<br />
|
||||
<Button variant={ButtonVariant.primary} onClick={deleteModal}>
|
||||
Delete Modal
|
||||
</Button>
|
||||
<br />
|
||||
<br />
|
||||
<Button variant={ButtonVariant.primary} onClick={updateModal}>
|
||||
</Button>
|
||||
<br />
|
||||
<br />
|
||||
<Button variant={ButtonVariant.primary} onClick={updateModal}>
|
||||
Update Modal
|
||||
</Button>
|
||||
</>
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
There are three buttons in the AppLayout component: create modal, delete modal, and update modal. Each modal is mapped with the corresponding `modalType`: `CREATE_MODAL`, `DELETE_MODAL`, or `UPDATE_MODAL`.
|
||||
There are three buttons in the AppLayout component: create modal, delete modal, and update modal. Each modal is mapped with the corresponding `modalType` : `CREATE_MODAL`, `DELETE_MODAL`, or `UPDATE_MODAL`.
|
||||
|
||||
### Use global dialogs
|
||||
|
||||
@ -315,22 +310,20 @@ Global modals are a clean and efficient way to handle dialogs in React. They are
|
||||
|
||||
If you'd like to see the code in action, I've included the [complete application][4] I created for this article in a sandbox.
|
||||
|
||||
Leslie Hinson sits down with Andrés Galante, an expert HTML and CSS coder who travels the world...
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/5/global-modals-react
|
||||
|
||||
作者:[Ajay Pratap][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][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/ajaypratap
|
||||
[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)
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/browser_web_internet_website.png
|
||||
[2]: https://reactjs.org/
|
||||
[3]: https://www.patternfly.org/v4/
|
||||
[4]: https://codesandbox.io/s/affectionate-pine-gib74
|
||||
|
@ -1,18 +1,20 @@
|
||||
[#]: subject: (Get started with Java serverless functions)
|
||||
[#]: via: (https://opensource.com/article/21/6/java-serverless-functions)
|
||||
[#]: author: (Daniel Oh https://opensource.com/users/daniel-oh)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: "Get started with Java serverless functions"
|
||||
[#]: via: "https://opensource.com/article/21/6/java-serverless-functions"
|
||||
[#]: author: "Daniel Oh https://opensource.com/users/daniel-oh"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Get started with Java serverless functions
|
||||
======
|
||||
Quarkus allows you to develop serverless workloads with familiar Java
|
||||
technology.
|
||||
Quarkus allows you to develop serverless workloads with familiar Java technology.
|
||||
|
||||
![Tips and gears turning][1]
|
||||
|
||||
Image by: opensource.com
|
||||
|
||||
The [serverless Java][2] journey started out with functions—small snippets of code running on demand. This phase didn't last long. Although functions based on virtual machine architecture in the 1.0 phase made this paradigm very popular, as the graphic below shows, there were limits around execution time, protocols, and poor local-development experience.
|
||||
|
||||
Developers then realized that they could apply the same serverless traits and benefits to microservices and Linux containers. This launched the 1.5 phase, where some serverless containers completely abstracted [Kubernetes][3], delivering the serverless experience through [Knative][4] or another abstraction layer that sits on top of it.
|
||||
@ -21,24 +23,21 @@ In the 2.0 phase, serverless starts to handle more complex orchestration and int
|
||||
|
||||
![The serverless Java journey][5]
|
||||
|
||||
(Daniel Oh, [CC BY-SA 4.0][6])
|
||||
|
||||
Before Java developers can start developing new serverless functions, their first task is to choose a new cloud-native Java framework that allows them to run Java functions quicker with a smaller memory footprint than traditional monolithic applications. This can be applied to various infrastructure environments, from physical servers to virtual machines to containers in multi- and hybrid-cloud environments.
|
||||
|
||||
Developers might consider an opinionated Spring framework that uses the `java.util.function` package in [Spring Cloud Function][7] to support the development of imperative and reactive functions. Spring also enables developers to deploy Java functions to installable serverless platforms such as [Kubeless][8], [Apache OpenWhisk][9], [Fission][10], and [Project Riff][11]. However, there are concerns about slow startup and response times and heavy memory-consuming processes with Spring. This problem can be worse when running Java functions on scalable container environments such as Kubernetes.
|
||||
Developers might consider an opinionated Spring framework that uses the `java.util.function` package in [Spring Cloud Function][6] to support the development of imperative and reactive functions. Spring also enables developers to deploy Java functions to installable serverless platforms such as [Kubeless][7], [Apache OpenWhisk][8], [Fission][9], and [Project Riff][10]. However, there are concerns about slow startup and response times and heavy memory-consuming processes with Spring. This problem can be worse when running Java functions on scalable container environments such as Kubernetes.
|
||||
|
||||
[Quarkus][12] is a new open source cloud-native Java framework that can help solve these problems. It aims to design serverless applications and write cloud-native microservices for running on cloud infrastructures (e.g., Kubernetes).
|
||||
[Quarkus][11] is a new open source cloud-native Java framework that can help solve these problems. It aims to design serverless applications and write cloud-native microservices for running on cloud infrastructures (e.g., Kubernetes).
|
||||
|
||||
Quarkus rethinks Java, using a closed-world approach to building and running it. It has turned Java into a runtime that's comparable to Go. Quarkus also includes more than 100 extensions that integrate enterprise capabilities, including database access, serverless integration, messaging, security, observability, and business automation.
|
||||
|
||||
Here is a quick example of how developers can scaffold a Java serverless function project with Quarkus.
|
||||
|
||||
### 1\. Create a Quarkus serverless Maven project
|
||||
### 1. Create a Quarkus serverless Maven project
|
||||
|
||||
Developers have multiple options to install a local Kubernetes cluster, including [Minikube][13] and [OKD][14] (OpenShift Kubernetes Distribution). This tutorial uses an OKD cluster for a developer's local environment because of the easy setup of serverless functionality on Knative and DevOps toolings. These guides for [OKD installation][15] and [Knative operator installation][16] offer more information about setting them up.
|
||||
|
||||
The following command generates a Quarkus project (e.g., `quarkus-serverless-restapi`) to expose a simple REST API and download a `quarkus-openshift` extension for Knative service deployment:
|
||||
Developers have multiple options to install a local Kubernetes cluster, including [Minikube][12] and [OKD][13] (OpenShift Kubernetes Distribution). This tutorial uses an OKD cluster for a developer's local environment because of the easy setup of serverless functionality on Knative and DevOps toolings. These guides for [OKD installation][14] and [Knative operator installation][15] offer more information about setting them up.
|
||||
|
||||
The following command generates a Quarkus project (e.g., `quarkus-serverless-restapi` ) to expose a simple REST API and download a `quarkus-openshift` extension for Knative service deployment:
|
||||
|
||||
```
|
||||
$ mvn io.quarkus:quarkus-maven-plugin:1.13.4.Final:create \
|
||||
@ -48,50 +47,45 @@ $ mvn io.quarkus:quarkus-maven-plugin:1.13.4.Final:create \
|
||||
-DclassName="org.acme.getting.started.GreetingResource"
|
||||
```
|
||||
|
||||
### 2\. Run serverless functions locally
|
||||
### 2. Run serverless functions locally
|
||||
|
||||
Run the application using Quarkus development mode to check if the REST API works, then tweak the code a bit:
|
||||
|
||||
|
||||
```
|
||||
`$ ./mvnw quarkus:dev`
|
||||
$ ./mvnw quarkus:dev
|
||||
```
|
||||
|
||||
The output will look like this:
|
||||
|
||||
|
||||
```
|
||||
__ ____ __ _____ ___ __ ____ ______
|
||||
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
|
||||
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
|
||||
\--\\___\\_\\____/_/ |_/_/|_/_/|_|\\____/___/
|
||||
INFO [io.quarkus] (Quarkus Main Thread) quarkus-serverless-restapi 1.0.0-SNAPSHOT on JVM (powered by Quarkus xx.xx.xx.) started in 2.386s. Listening on: <http://localhost:8080>
|
||||
__ ____ __ _____ ___ __ ____ ______
|
||||
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
|
||||
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
|
||||
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
|
||||
INFO [io.quarkus] (Quarkus Main Thread) quarkus-serverless-restapi 1.0.0-SNAPSHOT on JVM (powered by Quarkus xx.xx.xx.) started in 2.386s. Listening on: http://localhost:8080
|
||||
INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
|
||||
INFO [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, kubernetes, resteasy]
|
||||
```
|
||||
|
||||
> **Note**: Keep your Quarkus application running to use Live Coding. This allows you to avoid having to rebuild, redeploy the application, and restart the runtime whenever the code changes.
|
||||
|
||||
Now you can hit the REST API with a quick `curl` command. The output should be `Hello RESTEasy`:
|
||||
|
||||
Now you can hit the REST API with a quick `curl` command. The output should be `Hello RESTEasy` :
|
||||
|
||||
```
|
||||
$ curl localhost:8080/hello
|
||||
Hello RESTEasy
|
||||
```
|
||||
|
||||
Tweak the return text in `GreetingResource.java`:
|
||||
|
||||
Tweak the return text in `GreetingResource.java` :
|
||||
|
||||
```
|
||||
public [String][17] hello() {
|
||||
public String hello() {
|
||||
return "Quarkus Function on Kubernetes";
|
||||
}
|
||||
```
|
||||
|
||||
You will see new output when you reinvoke the REST API:
|
||||
|
||||
|
||||
```
|
||||
$ curl localhost:8080/hello
|
||||
Quarkus Function on Kubernetes
|
||||
@ -99,87 +93,77 @@ Quarkus Function on Kubernetes
|
||||
|
||||
There's not been a big difference between normal microservices and serverless functions. A benefit of Quarkus is that it enables developers to use any microservice to deploy Kubernetes as a serverless function.
|
||||
|
||||
### 3\. Deploy the functions to a Knative service
|
||||
### 3. Deploy the functions to a Knative service
|
||||
|
||||
If you haven't already, [create a namespace][18] (e.g., `quarkus-serverless-restapi`) on your OKD (Kubernetes) cluster to deploy this Java serverless function.
|
||||
|
||||
Quarkus enables developers to generate Knative and Kubernetes resources by adding the following variables in `src/main/resources/application.properties`:
|
||||
If you haven't already, [create a namespace][16] (e.g., `quarkus-serverless-restapi` ) on your OKD (Kubernetes) cluster to deploy this Java serverless function.
|
||||
|
||||
Quarkus enables developers to generate Knative and Kubernetes resources by adding the following variables in `src/main/resources/application.properties` :
|
||||
|
||||
```
|
||||
quarkus.container-image.group=quarkus-serverless-restapi <1>
|
||||
quarkus.container-image.registry=image-registry.openshift-image-registry.svc:5000 <2>
|
||||
quarkus.kubernetes-client.trust-certs=true <3>
|
||||
quarkus.kubernetes.deployment-target=knative <4>
|
||||
quarkus.kubernetes.deploy=true <5>
|
||||
quarkus.openshift.build-strategy=docker <6>
|
||||
quarkus.container-image.group=quarkus-serverless-restapi <1>
|
||||
quarkus.container-image.registry=image-registry.openshift-image-registry.svc:5000 <2>
|
||||
quarkus.kubernetes-client.trust-certs=true <3>
|
||||
quarkus.kubernetes.deployment-target=knative <4>
|
||||
quarkus.kubernetes.deploy=true <5>
|
||||
quarkus.openshift.build-strategy=docker <6>
|
||||
```
|
||||
|
||||
> Legend:
|
||||
>
|
||||
> <1> Define a project name where you deploy a serverless application
|
||||
> <2> The container registry to use
|
||||
> <3> Use self-signed certs in this simple example to trust them
|
||||
> <4> Enable the generation of Knative resources
|
||||
> <5> Instruct the extension to deploy to OpenShift after the container image is built
|
||||
> <6> Set the Docker build strategy
|
||||
|
||||
> <1> Define a project name where you deploy a serverless application
|
||||
<2> The container registry to use
|
||||
<3> Use self-signed certs in this simple example to trust them
|
||||
<4> Enable the generation of Knative resources
|
||||
<5> Instruct the extension to deploy to OpenShift after the container image is built
|
||||
<6> Set the Docker build strategy
|
||||
|
||||
This command builds the application then deploys it directly to the OKD cluster:
|
||||
|
||||
|
||||
```
|
||||
`$ ./mvnw clean package -DskipTests`
|
||||
$ ./mvnw clean package -DskipTests
|
||||
```
|
||||
|
||||
> **Note:** Make sure to log in to the right project (e.g., `quarkus-serverless-restapi`) by using the `oc login` command ahead of time.
|
||||
> **Note:** Make sure to log in to the right project (e.g., `quarkus-serverless-restapi` ) by using the `oc login` command ahead of time.
|
||||
|
||||
The output should end with `BUILD SUCCESS`.
|
||||
|
||||
Add a Quarkus label to the Knative service with this `oc` command:
|
||||
|
||||
|
||||
```
|
||||
$ oc label rev/quarkus-serverless-restapi-00001
|
||||
$ oc label rev/quarkus-serverless-restapi-00001
|
||||
app.openshift.io/runtime=quarkus --overwrite
|
||||
```
|
||||
|
||||
Then access the OKD web console to go to the [Topology view in the Developer perspective][19]. You might see that your pod (serverless function) is already scaled down to zero (white-line circle).
|
||||
Then access the OKD web console to go to the [Topology view in the Developer perspective][17]. You might see that your pod (serverless function) is already scaled down to zero (white-line circle).
|
||||
|
||||
![Topology view][20]
|
||||
![Topology view][18]
|
||||
|
||||
(Daniel Oh, [CC BY-SA 4.0][6])
|
||||
|
||||
### 4\. Test the functions on Kubernetes
|
||||
### 4. Test the functions on Kubernetes
|
||||
|
||||
Retrieve a route `URL` of the serverless function by running the following `oc` command:
|
||||
|
||||
|
||||
```
|
||||
$ oc get rt/quarkus-serverless-restapi
|
||||
[...]
|
||||
NAME URL READY REASON
|
||||
quarkus-serverless[...] <http://quarkus\[...\].SUBDOMAIN> True
|
||||
quarkus-serverless[...] http://quarkus[...].SUBDOMAIN True
|
||||
```
|
||||
|
||||
Access the route `URL` with a `curl` command:
|
||||
|
||||
|
||||
```
|
||||
`$ curl http://quarkus-serverless-restapi-quarkus-serverless-restapi.SUBDOMAIN/hello`
|
||||
$ curl http://quarkus-serverless-restapi-quarkus-serverless-restapi.SUBDOMAIN/hello
|
||||
```
|
||||
|
||||
In a few seconds, you will get the same result as you got locally:
|
||||
|
||||
|
||||
```
|
||||
`Quarkus Function on Kubernetes`
|
||||
Quarkus Function on Kubernetes
|
||||
```
|
||||
|
||||
When you return to the Topology view in the OKD cluster, the Knative service scales up automatically.
|
||||
|
||||
![Scaling the Knative Function][21]
|
||||
|
||||
(Daniel Oh, [CC BY-SA 4.0][6])
|
||||
![Scaling the Knative Function][19]
|
||||
|
||||
This Knative service pod will go down to zero again in 30 seconds because of Knative serving's default setting.
|
||||
|
||||
@ -189,37 +173,37 @@ The serverless journey has evolved, starting with functions on virtual machines
|
||||
|
||||
The next article in this series will guide you on optimizing Java serverless functions in Kubernetes for faster startup time and small memory footprints at scale.
|
||||
|
||||
Image by: (Daniel Oh, CC BY-SA 4.0)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/6/java-serverless-functions
|
||||
|
||||
作者:[Daniel Oh][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][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/daniel-oh
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/gears_devops_learn_troubleshooting_lightbulb_tips_520.png?itok=HcN38NOk (Tips and gears turning)
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/gears_devops_learn_troubleshooting_lightbulb_tips_520.png
|
||||
[2]: https://opensource.com/article/21/5/what-serverless-java
|
||||
[3]: https://opensource.com/article/19/6/reasons-kubernetes
|
||||
[4]: https://cloud.google.com/knative/
|
||||
[5]: https://opensource.com/sites/default/files/uploads/serverless-journey.png (The serverless Java journey)
|
||||
[6]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[7]: https://spring.io/serverless
|
||||
[8]: https://kubeless.io/
|
||||
[9]: https://openwhisk.apache.org/
|
||||
[10]: https://fission.io/
|
||||
[11]: https://projectriff.io/
|
||||
[12]: https://quarkus.io/
|
||||
[13]: https://minikube.sigs.k8s.io/docs/start/
|
||||
[14]: https://docs.okd.io/latest/welcome/index.html
|
||||
[15]: https://docs.okd.io/latest/installing/index.html
|
||||
[16]: https://knative.dev/docs/install/knative-with-operators/
|
||||
[17]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+string
|
||||
[18]: https://docs.okd.io/latest/applications/projects/configuring-project-creation.html
|
||||
[19]: https://docs.okd.io/latest/applications/application_life_cycle_management/odc-viewing-application-composition-using-topology-view.html
|
||||
[20]: https://opensource.com/sites/default/files/uploads/topologyview.png (Topology view)
|
||||
[21]: https://opensource.com/sites/default/files/uploads/scale-up-knative-function.png (Scaling the Knative Function)
|
||||
[5]: https://opensource.com/sites/default/files/uploads/serverless-journey.png
|
||||
[6]: https://spring.io/serverless
|
||||
[7]: https://kubeless.io/
|
||||
[8]: https://openwhisk.apache.org/
|
||||
[9]: https://fission.io/
|
||||
[10]: https://projectriff.io/
|
||||
[11]: https://quarkus.io/
|
||||
[12]: https://minikube.sigs.k8s.io/docs/start/
|
||||
[13]: https://docs.okd.io/latest/welcome/index.html
|
||||
[14]: https://docs.okd.io/latest/installing/index.html
|
||||
[15]: https://knative.dev/docs/install/knative-with-operators/
|
||||
[16]: https://docs.okd.io/latest/applications/projects/configuring-project-creation.html
|
||||
[17]: https://docs.okd.io/latest/applications/application_life_cycle_management/odc-viewing-application-composition-using-topology-view.html
|
||||
[18]: https://opensource.com/sites/default/files/uploads/topologyview.png
|
||||
[19]: https://opensource.com/sites/default/files/uploads/scale-up-knative-function.png
|
||||
|
@ -1,209 +0,0 @@
|
||||
[#]: subject: (9 reasons I love to use the Qt Creator IDE)
|
||||
[#]: via: (https://opensource.com/article/21/6/qtcreator)
|
||||
[#]: author: (Stephan Avenwedde https://opensource.com/users/hansic99)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (hadisi1993)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
|
||||
9 reasons I love to use the Qt Creator IDE
|
||||
======
|
||||
Qt Creator is the glue between Qt's rich set of libraries and the
|
||||
programmer.
|
||||
![Business woman on laptop sitting in front of window][1]
|
||||
|
||||
Qt Creator is the Qt framework's default integrated development environment (IDE) and hence the glue between Qt's rich set of libraries and the user. In addition to its basic features such as intelligent code completion, debugging, and project administration, Qt Creator offers a lot of nice features that make software development easier.
|
||||
|
||||
In this article, I will highlight some of my favorite [Qt Creator][2] features.
|
||||
|
||||
### Dark mode
|
||||
|
||||
My first question when working with a new application is: _Is there a dark mode?_ Qt Creator answers with: _Which dark mode do you prefer?_
|
||||
|
||||
You can activate dark mode in the Options menu. On the top menu bar, go to **Tools**, select **Options**, and go to the **Environment** section. Here is where you can select the general appearance:
|
||||
|
||||
![ QT Creator dark mode][3]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
### Custom appearance
|
||||
|
||||
Like every Qt application, Qt Creator's appearance is highly customizable with style sheets. Below, you can follow along with my approach to give Qt Creator a fancy look.
|
||||
|
||||
Create the file `mycustomstylesheet.css` with the following content:
|
||||
|
||||
|
||||
```
|
||||
QMenuBar { background-color: olive }
|
||||
QMenuBar::item { background-color: olive }
|
||||
QMenu { background-color : beige; color : black }
|
||||
QLabel { color: green }
|
||||
```
|
||||
|
||||
Then start Qt Creator from the command line and pass the style sheet as a parameter with:
|
||||
|
||||
|
||||
```
|
||||
`qtcreator -stylesheet=mycustomstylesheet.css`
|
||||
```
|
||||
|
||||
It should look like this:
|
||||
|
||||
![QT Creator custom stylesheet][5]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
Read more about style sheets in the [documentation][6].
|
||||
|
||||
### Command-line parameters
|
||||
|
||||
Qt Creator accepts many command-line options. For example, if you want to automatically load your current project at startup, pass the path to the `*.pro-file`:
|
||||
|
||||
|
||||
```
|
||||
`qtcreator ~/MyProject/MyQtProject.pro`
|
||||
```
|
||||
|
||||
You can even pass the file and the line number that should be opened by default. This command opens the file `main.cpp` at line 20:
|
||||
|
||||
|
||||
```
|
||||
`qtcreator ~/MyProject/main.cpp:20`
|
||||
```
|
||||
|
||||
Read more about the Qt Creator-specific command-line options in the [documentation][7].
|
||||
|
||||
Qt Creator is an ordinary Qt application, so, in addition to its own command-line arguments, it also accepts the generic arguments for [QApplication][8] and [QGuiApplication][9].
|
||||
|
||||
### Cross-compiling
|
||||
|
||||
Qt Creator allows you to define several toolchains, called **Kits**. A kit defines the binaries and SDK for building and running an application:
|
||||
|
||||
![QT Creator kits][10]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
This allows you to switch between completely different toolchains with just two clicks:
|
||||
|
||||
![Switching between Kits in Qt Creator][11]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
Read more about kits in the [manual][12].
|
||||
|
||||
### Analyzer
|
||||
|
||||
Qt Creator integrates several of the most popular analyzers, such as:
|
||||
|
||||
* [Linux Performance Analyzer][13] (requires a special kernel)
|
||||
* [Valgrind][14] memory profiler
|
||||
* [Clang-Tidy and Clazy][15], a linter for C/C++
|
||||
|
||||
|
||||
|
||||
![Qt Creator analyzer][16]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
### Debugger
|
||||
|
||||
When it comes to debugging, Qt Creator has a nice interface for GNU Debugger (GDB). I like its easy way of inspecting container types and creating conditional breakpoints:
|
||||
|
||||
![Qt Creator debugger][17]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
### FakeVim
|
||||
|
||||
If you like Vim, enable FakeVim in the settings to control Qt Creator like Vim. Go to **Tools** and select **Options**. In the **FakeVim** section, you can find many switches to customize FakeVim's behavior. In addition to the editor functions, you can also map your own functions to custom Vim commands.
|
||||
|
||||
For example, you can map the function **Build Project** to the `build` command:
|
||||
|
||||
![FakeVim in Qt Creator][18]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
Back in the editor, when you press the colon button and enter `build`, Qt Creator starts a build process with the configured toolchain:
|
||||
|
||||
![FakeVim in Qt Creator][19]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
You can find more information about FakeVim [in the docs][20].
|
||||
|
||||
### Class inspector
|
||||
|
||||
When developing in C++, open the right window by clicking on the button in the bottom-right corner of Qt Creator. Then choose **Outline** from the dropdown menu on the top border. If you have a header file open on the left pane, you get a nice overview of the defined classes or types. If you switch to a source file (`*.cpp`), the right pane will list all defined methods, and you can jump to one by double-clicking on it:
|
||||
|
||||
![List of classes in Qt Creator][21]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
### Project configuration
|
||||
|
||||
Qt Creator projects are built around the `*.pro-file` in the project's directory. You can add your own custom configuration to the project's `*.pro-file` of your project. I added `my_special_config` to the `*.pro-file`, which adds `MY_SPECIAL_CONFIG` to the compiler defined:
|
||||
|
||||
|
||||
```
|
||||
QT -= gui
|
||||
|
||||
CONFIG += c++11 console
|
||||
CONFIG -= app_bundle
|
||||
|
||||
CONFIG += my_special_config
|
||||
|
||||
my_special_config {
|
||||
DEFINES += MY_SPECIAL_CONFIG
|
||||
}
|
||||
```
|
||||
|
||||
Qt Creator automatically highlights the code according to the active configuration:
|
||||
|
||||
![Special configuration in Qt Creator][22]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
The `*.pro-file` is written in the [qmake language][23].
|
||||
|
||||
### Summary
|
||||
|
||||
These features are only the tip of the iceberg of what Qt Creator provides. Beginners shouldn't feel overwhelmed by the many features, as Qt Creator is absolutely beginner-friendly. It may even be the easiest way to start developing in C++. To get a complete overview of its features, refer to the [official Qt Creator documentation][24].
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/6/qtcreator
|
||||
|
||||
作者:[Stephan Avenwedde][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/hansic99
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/lenovo-thinkpad-laptop-concentration-focus-windows-office.png?itok=-8E2ihcF (Woman using laptop concentrating)
|
||||
[2]: https://www.qt.io/product/development-tools
|
||||
[3]: https://opensource.com/sites/default/files/uploads/qt_creator_dark_mode.png ( QT Creator dark mode)
|
||||
[4]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[5]: https://opensource.com/sites/default/files/uploads/qt_creator_custom_stylesheet2.png (QT Creator custom stylesheet)
|
||||
[6]: https://doc.qt.io/qt-5/stylesheet-reference.html
|
||||
[7]: https://doc.qt.io/qtcreator/creator-cli.html
|
||||
[8]: https://doc.qt.io/qt-5/qapplication.html#QApplication
|
||||
[9]: https://doc.qt.io/qt-5/qguiapplication.html#supported-command-line-options
|
||||
[10]: https://opensource.com/sites/default/files/uploads/qt_creator_cross_compiling.png (QT Creator kits)
|
||||
[11]: https://opensource.com/sites/default/files/uploads/qt_creator_select_kits.png (Switching between Kits in Qt Creator)
|
||||
[12]: https://doc.qt.io/qtcreator/creator-targets.html
|
||||
[13]: https://doc.qt.io/qtcreator/creator-cpu-usage-analyzer.html
|
||||
[14]: https://doc.qt.io/qtcreator/creator-valgrind-overview.html
|
||||
[15]: https://doc.qt.io/qtcreator/creator-clang-tools.html
|
||||
[16]: https://opensource.com/sites/default/files/uploads/qt_creator_analyzer.png (Qt Creator analyzer)
|
||||
[17]: https://opensource.com/sites/default/files/uploads/qt_creator_debugger2.png (Qt Creator debugger)
|
||||
[18]: https://opensource.com/sites/default/files/uploads/qt_creator_fakevim_ex_commands.png (FakeVim in Qt Creator)
|
||||
[19]: https://opensource.com/sites/default/files/uploads/qt_creator_fakevim_build_commands.png (FakeVim in Qt Creator)
|
||||
[20]: https://doc.qt.io/qtcreator/creator-editor-fakevim.html
|
||||
[21]: https://opensource.com/sites/default/files/uploads/qtcreator_class_overview.png (List of classes in Qt Creator)
|
||||
[22]: https://opensource.com/sites/default/files/uploads/qtcreater_special_config.png (Special configuration in Qt Creator)
|
||||
[23]: https://doc.qt.io/qt-5/qmake-language.html
|
||||
[24]: https://doc.qt.io/qtcreator/
|
@ -0,0 +1,253 @@
|
||||
[#]: subject: "The Definitive Guide to Using and Customizing the Dock in Ubuntu"
|
||||
[#]: via: "https://itsfoss.com/customize-ubuntu-dock/"
|
||||
[#]: author: "Abhishek Prakash https://itsfoss.com/author/abhishek/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
The Definitive Guide to Using and Customizing the Dock in Ubuntu
|
||||
======
|
||||
When you log into Ubuntu, you’ll see the dock on the left side with some application icons on it. This dock (also known as launcher or sometimes as panel) allows you to quickly launch your frequently used programs.
|
||||
|
||||
![Ubuntu Dock][1]
|
||||
|
||||
I rely heavily on the dock and I am going to share a few tips about using the dock effectively and customize its looks and position.
|
||||
|
||||
You’ll learn the following in this tutorial:
|
||||
|
||||
* Basic usage of the dock: adding more applications and using shortcuts for launching applications.
|
||||
* Customize the looks of the dock: Change the icon size, icon positions.
|
||||
* Change the position: for single screen and multi-monitor setup
|
||||
* Hide mounted disk from the dock
|
||||
* Auto-hide or disable the dock
|
||||
* Possibility of additional dock customization with dconf-editor
|
||||
* Replace dock with other docking applications
|
||||
|
||||
I’ll use the terms dock, panel and launcher in the tutorial. All of them refer to the same thing.
|
||||
|
||||
### Using the Ubuntu dock: Absolute basic that you must know
|
||||
|
||||
If you are new to Ubuntu, you should know a few things about using the dock. You’ll eventually discover these dock features, I’ll just speed up the discovery process for you.
|
||||
|
||||
![A Video from YouTube][2]
|
||||
|
||||
[Subscribe to our YouTube channel for more Linux videos][3]
|
||||
|
||||
#### Add new applications to the dock (or remove them)
|
||||
|
||||
The steps are simple. Search for the application from the menu and run it.
|
||||
|
||||
The running application appears in the dock, below all other icons. Right click on it and select the “Add to Favorites” option. This will lock the icon to the dock.
|
||||
|
||||
![Right click on the icon and select "Add to Favorites" to add icons to the dock in Ubuntu][4]
|
||||
|
||||
Removing an app icon from the doc is even easier. You don’t even need to run the application. Simply right click on it and select “Remove From Favorites”.
|
||||
|
||||
![Right-click on the icon and select "Remove from Favorites" to remove icons from the dock in Ubuntu][5]
|
||||
|
||||
#### Reorder icon position
|
||||
|
||||
By default, new application icons are added after all the other icons on the launcher. You don’t have to live with it as it is.
|
||||
|
||||
To change the order of the icons, you just need to drag and drop to the other position of your choice. No need to “lock it” or any additional effort. It stays on that location until you make some changes again.
|
||||
|
||||
![Reorder Icons On Ubuntu Docks][6]
|
||||
|
||||
#### Right click to get additional options for some apps
|
||||
|
||||
Left-clicking on an icon launches the application or bring it to focus if the application is already running.
|
||||
|
||||
Right-clicking the icon gives you additional options. Different applications will have different options.
|
||||
|
||||
For browsers, you can open a new private window or preview all the running windows.
|
||||
|
||||
![Right Click Icons Ubuntu Dock][7]
|
||||
|
||||
For file manager, you can go to all the bookmarked directories or preview opened windows.
|
||||
|
||||
You can, of course, quit the application. Most applications will quit while some applications like Telegram will be minimized to the system tray.
|
||||
|
||||
#### Use keyboard shortcut to launch applications quickly [Not many people know about this one]
|
||||
|
||||
The dock allows you to launch an application in a single mouse click. But if you are like me, you can save that mouse click with a keyboard shortcut.
|
||||
|
||||
Using the Super/Window key and a number key will launch the application on that position.
|
||||
|
||||
![Keyboard Shortcut For Ubuntu Dock][8]
|
||||
|
||||
If the application is already running, it is brought to focus, i.e. it appears in front of all the other running application windows.
|
||||
|
||||
Since it is position-based, you should make sure that you don’t reorder the icons all the time. Personally, I keep Firefox at position 1, file manager at 2 and the alternate browser at 3 and so on until number 9. This way, I quickly launch the file manager with Super+2.
|
||||
|
||||
I find it easier specially because I have a three screen setup and moving the mouse to the launcher on the first screen is a bit too much of trouble. You can enable or disable the dock on additional screen. I’ll show that to you later in this tutorial.
|
||||
|
||||
### Change the position of the dock
|
||||
|
||||
By default, the dock is located on the left side of your screen. Some people like the launcher at the bottom, in a more traditional way.
|
||||
|
||||
[Ubuntu allows you to change the position of the dock][9]. You can move it to the bottom or to the right side. I am not sure many people actually put the dock on the top, so moving the dock to the top is not an option here.
|
||||
|
||||
![Change Launcher Position in Ubuntu][10]
|
||||
|
||||
To change the dock position, go to Settings->Appearance. You should see some options under Dock section. You need to change the “Position on screen” settings here.
|
||||
|
||||
![Change Dock Position in Ubuntu][11]
|
||||
|
||||
#### Position of dock on a multiple monitor setup
|
||||
|
||||
If you have multiple screens attached to your system, you can choose whether to display the dock on all screens or one of the chosen screens.
|
||||
|
||||
![Ubuntu Dock Settings Multimonitor][12]
|
||||
|
||||
Personally, I display the dock on my laptop screen only which is my main screen. This gives me maximum space on the additional two screens.
|
||||
|
||||
### Change the appearance of the dock
|
||||
|
||||
Let’s see some more dock customization options in Ubuntu.
|
||||
|
||||
Imagine you added too many applications to the dock or have too many applications open. It will fill up the space and you’ll have to scroll to the top and bottom to go to the applications at end points.
|
||||
|
||||
What you can do here is to change the icon size and the dock will now accommodate more icons. Don’t make it too small, though.
|
||||
|
||||
![Normal Icon Size Dock][13]
|
||||
|
||||
![Smaller Icon Size Dock][14]
|
||||
|
||||
To do that, go to Settings-> Appearance and change it by moving the slider under Icon size. The default icons size is 48 pixels.
|
||||
|
||||
![Changing Icon Size In Ubuntu Dock][15]
|
||||
|
||||
#### Hide mounted disks from the launcher
|
||||
|
||||
If you plug in a USB disk or SD Card, it is mounted to the system, and an icon appear in the launcher immediately. This is helpful because you can right click on it and select safely remove drive option.
|
||||
|
||||
![External Mounted Disks In Ubuntu Dock][16]
|
||||
|
||||
If you somehow find it troublesome, you can turn this feature off. Don’t worry, you can still access the mounted drives from the file manager.
|
||||
|
||||
Open a terminal and use the following command:
|
||||
|
||||
```
|
||||
gsettings set org.gnome.shell.extensions.dash-to-dock show-mounts false
|
||||
```
|
||||
|
||||
The changes take into effect immediately. You won’t be bothered with mounted disk being displayed in the launcher.
|
||||
|
||||
If you want the default behavior back, use this command:
|
||||
|
||||
```
|
||||
gsettings set org.gnome.shell.extensions.dash-to-dock show-mounts true
|
||||
```
|
||||
|
||||
### Change the behavior of dock
|
||||
|
||||
Let’s customize the default behavior of the dock and make it more suitable to your needs.
|
||||
|
||||
#### Enable minimize on click
|
||||
|
||||
If you click on the icon of a running application, its window will be brought to focus. That’s fine. However, if you click on it, nothing happens. By default, clicking on the same icon won’t minimize the application.
|
||||
|
||||
Well, this is the behavior in modern desktop, but I don’t like it. I prefer that the application is minimized when I click on its icon for the second time.
|
||||
|
||||
If you are like me, you may want to [enable click to minimize option in Ubuntu][17]:
|
||||
|
||||
![A Video from YouTube][18]
|
||||
|
||||
To do that, open a terminal and enter the following command:
|
||||
|
||||
```
|
||||
gsettings set org.gnome.shell.extensions.dash-to-dock click-action 'minimize'
|
||||
```
|
||||
|
||||
#### Auto-hide Ubuntu dock and get more screen space
|
||||
|
||||
If you want to utilize the maximum screen space, you can enable auto-hide option for the dock in Ubuntu.
|
||||
|
||||
This will hide the dock, and you’ll get the entire screen. The dock is still accessible, though. Move your cursor to the location of the dock where it used to be, and it will appear again. When the dock reappears, it is overlaid on the running application window. And that’s a good thing otherwise too many elements would start moving on the screen.
|
||||
|
||||
The auto-hide option is available in Settings-> Appearance and under Dock section. Just toggle it.
|
||||
|
||||
![Autohide the Dock Ubuntu][19]
|
||||
|
||||
If you don’t like this behavior, you can enable it again the same way.
|
||||
|
||||
#### Disable Ubuntu dock
|
||||
|
||||
Auto-hide option is good enough for many people, but some users simply don’t like the dock. If you are one of those users, you also have the option to disable the Ubuntu dock entirely.
|
||||
|
||||
Starting with Ubuntu 20.04, you have the Extensions application available at your disposal to [manage GNOME Extensions][20].
|
||||
|
||||
![Gnome Extensions App Ubuntu][21]
|
||||
|
||||
With this Extensions application, you can easily disable or re-enable the dock.
|
||||
|
||||
![Disable Dock Ubuntu][22]
|
||||
|
||||
### Advanced dock customization with dconf-editor [Not recommended]
|
||||
|
||||
##### Warning
|
||||
|
||||
The dconf-editor allows you to change almost every aspect of the GNOME desktop environment. This is both good and bad because you must be careful in editing. Most of the settings can be changed on the fly, without asking for conformation. While you may reset the changes, you could still put your system in such a state that it would be difficult to put things back in order.For this reason, I advise not to play with dconf-editor, specially if you don’t like spending time in troubleshooting and fixing problems or if you are not too familiar with Linux and GNOME.
|
||||
|
||||
The [dconf editor][23] gives you additional options to customize the dock in Ubuntu. Install it from the software center and then navigate to org > gnome > shell > extensions > dash-to-dock. You’ll find plenty of options here. I cannot even list them all here.
|
||||
|
||||
![Dconf Editor Dock][24]
|
||||
|
||||
### Replace the dock in Ubuntu
|
||||
|
||||
There are several third-party dock applications available for Ubuntu and other Linux distributions. You can install a dock of your choice and use it.
|
||||
|
||||
For example, you can install Plank dock from the software center and use it in similar fashion to Ubuntu dock.
|
||||
|
||||
![Plank Dock Ubuntu][25]
|
||||
|
||||
Disabling Ubuntu Dock would be a better idea in this case. It won’t be wise to use multiple docks at the same time.
|
||||
|
||||
### Conclusion
|
||||
|
||||
This tutorial is about customizing the default dock or launcher provided in Ubuntu’s GNOME implementation. Some suggestions should work on the dock in vanilla GNOME as work well.
|
||||
|
||||
I have shown you most of the common Ubuntu dock customization. You don’t need to go and blindly follow all of them. Read and think which one suits your need and then act upon it.
|
||||
|
||||
Was it too trivial or did you learn something new? Would you like to see more such tutorials? I welcome your suggestions and feedback on dock customization.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/customize-ubuntu-dock/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lkxed][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/lkxed
|
||||
[1]: https://itsfoss.com/wp-content/uploads/2021/01/ubuntu-dock.png
|
||||
[2]: https://player.vimeo.com/video/534830884
|
||||
[3]: https://www.youtube.com/c/itsfoss?sub_confirmation=1
|
||||
[4]: https://itsfoss.com/wp-content/uploads/2021/01/add-icons-to-dock.png
|
||||
[5]: https://itsfoss.com/wp-content/uploads/2021/01/remove-icons-from-dock.png
|
||||
[6]: https://itsfoss.com/wp-content/uploads/2021/01/reorder-icons-on-ubuntu-docks-800x430.gif
|
||||
[7]: https://itsfoss.com/wp-content/uploads/2021/01/right-click-icons-ubuntu-dock.png
|
||||
[8]: https://itsfoss.com/wp-content/uploads/2021/01/keyboard-shortcut-for-ubuntu-dock.png
|
||||
[9]: https://itsfoss.com/move-unity-launcher-bottom/
|
||||
[10]: https://itsfoss.com/wp-content/uploads/2021/01/change-launcher-position-ubuntu.png
|
||||
[11]: https://itsfoss.com/wp-content/uploads/2021/01/change-dock-position-ubuntu.png
|
||||
[12]: https://itsfoss.com/wp-content/uploads/2021/01/ubuntu-dock-settings-multimonitor.png
|
||||
[13]: https://itsfoss.com/wp-content/uploads/2021/01/normal-icon-size-dock.jpg
|
||||
[14]: https://itsfoss.com/wp-content/uploads/2021/01/smaller-icon-size-dock.jpg
|
||||
[15]: https://itsfoss.com/wp-content/uploads/2021/01/changing-icon-size-in-ubuntu-dock.png
|
||||
[16]: https://itsfoss.com/wp-content/uploads/2021/01/external-mounted-disks-in-ubuntu-dock.png
|
||||
[17]: https://itsfoss.com/click-to-minimize-ubuntu/
|
||||
[18]: https://giphy.com/embed/52FlrSIMxnZ1qq9koP
|
||||
[19]: https://itsfoss.com/wp-content/uploads/2021/01/autohide-dock-ubuntu.png
|
||||
[20]: https://itsfoss.com/gnome-shell-extensions/
|
||||
[21]: https://itsfoss.com/wp-content/uploads/2020/06/GNOME-extensions-app-ubuntu.jpg
|
||||
[22]: https://itsfoss.com/wp-content/uploads/2021/01/disable-dock-ubuntu.png
|
||||
[23]: https://wiki.gnome.org/Apps/DconfEditor
|
||||
[24]: https://itsfoss.com/wp-content/uploads/2021/01/dconf-editor-dock.png
|
||||
[25]: https://itsfoss.com/wp-content/uploads/2021/01/plank-dock-Ubuntu-800x382.jpg
|
@ -1,183 +0,0 @@
|
||||
[#]: subject: "A guide to Kubernetes architecture"
|
||||
[#]: via: "https://opensource.com/article/22/2/kubernetes-architecture"
|
||||
[#]: author: "Nived Velayudhan https://opensource.com/users/nivedv"
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: "MjSeven"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
A guide to Kubernetes architecture
|
||||
======
|
||||
Learn how the different components of Kubernetes architecture fit
|
||||
together so you can be better equipped to diagnose problems, maintain a
|
||||
healthy cluster, and optimize your own workflow.
|
||||
![Parts, modules, containers for software][1]
|
||||
|
||||
You use Kubernetes to orchestrate containers. It's an easy description to say, but understanding what that actually means and how you accomplish it is another matter entirely. If you're running or managing a Kubernetes cluster, then you know that Kubernetes consists of one computer that gets designated as the _control plane_, and lots of other computers that get designated as _worker nodes_. Each of these has a complex but robust stack making orchestration possible, and getting familiar with each component helps understand how it all works.
|
||||
|
||||
![Kubernetes architecture diagram][2]
|
||||
|
||||
(Nived Velayudhan, [CC BY-SA 4.0][3])
|
||||
|
||||
### Control plane components
|
||||
|
||||
You install Kubernetes on a machine called the control plane. It's the one running the Kubernetes daemon, and it's the one you communicate with when starting containers and pods. The following sections describe the control plane components.
|
||||
|
||||
#### Etcd
|
||||
|
||||
Etcd is a fast, distributed, and consistent key-value store used as a backing store for persistently storing Kubernetes object data such as pods, replication controllers, secrets, and services. Etcd is the only place where Kubernetes stores cluster state and metadata. The only component that talks to etcd directly is the Kubernetes API server. All other components read and write data to etcd indirectly through the API server.
|
||||
|
||||
Etcd also implements a watch feature, which provides an event-based interface for asynchronously monitoring changes to keys. Once you change a key, its watchers get notified. The API server component heavily relies on this to get notified and move the current state of etcd towards the desired state.
|
||||
|
||||
_Why should the number of etcd instances be an odd number?_
|
||||
|
||||
You would typically have three, five, or seven etcd instances running in a high-availability (HA) environment, but why? Because etcd is a distributed data store. It is possible to scale it horizontally but also you need to ensure that the data in each instance is consistent, and for this, your system needs to reach a consensus on what the state is. Etcd uses the [RAFT consensus algorithm][4] for this.
|
||||
|
||||
The algorithm requires a majority (or quorum) for the cluster to progress to the next state. If you have only two ectd instances and either of them fails, the etcd cluster can't transition to a new state because no majority exists. If you have three ectd instances, one instance can fail but still have a majority of instances available to reach a quorum.
|
||||
|
||||
#### API server
|
||||
|
||||
The API server is the only component in Kubernetes that directly interacts with etcd. All other components in Kubernetes must go through the API server to work with the cluster state, including the clients (kubectl). The API server has the following functions:
|
||||
|
||||
* Provides a consistent way of storing objects in etcd.
|
||||
* Performs validation of those objects so clients can't store improperly configured objects (which could happen if they write directly to the etcd datastore).
|
||||
* Provides a RESTful API to create, update, modify, or delete a resource.
|
||||
* Provides [optimistic concurrency locking][5], so other clients never override changes to an object in the event of concurrent updates.
|
||||
* Performs authentication and authorization of a request that the client sends. It uses the plugins to extract the client's username, user ID, groups the user belongs to, and determine whether the authenticated user can perform the requested action on the requested resource.
|
||||
* Responsible for [admission control][6] if the request is trying to create, modify, or delete a resource. For example, AlwaysPullImages, DefaultStorageClass, and ResourceQuota.
|
||||
* Implements a watch mechanism (similar to etcd) for clients to watch for changes. This allows components such as the Scheduler and Controller Manager to interact with the API Server in a loosely coupled manner.
|
||||
|
||||
|
||||
|
||||
#### Controller Manager
|
||||
|
||||
In Kubernetes, controllers are control loops that watch the state of your cluster, then make or request changes where needed. Each controller tries to move the current cluster state closer to the desired state. The controller tracks at least one Kubernetes resource type, and these objects have a spec field that represents the desired state.
|
||||
|
||||
Controller examples:
|
||||
|
||||
* Replication Manager (a controller for ReplicationController resources)
|
||||
* ReplicaSet, DaemonSet, and Job controllers
|
||||
* Deployment controller
|
||||
* StatefulSet controller
|
||||
* Node controller
|
||||
* Service controller
|
||||
* Endpoints controller
|
||||
* Namespace controller
|
||||
* PersistentVolume controller
|
||||
|
||||
|
||||
|
||||
Controllers use the watch mechanism to get notified of changes. They watch the API server for changes to resources and perform operations for each change, whether it's a creation of a new object or an update or deletion of an existing object. Most of the time, these operations include creating other resources or updating the watched resources themselves. Still, because using watches doesn't guarantee the controller won't miss an event, they also perform a re-list operation periodically to ensure they haven't missed anything.
|
||||
|
||||
The Controller Manager also performs lifecycle functions such as namespace creation and lifecycle, event garbage collection, terminated-pod garbage collection, [cascading-deletion garbage collection][7], and node garbage collection. See [Cloud Controller Manager][8] for more information.
|
||||
|
||||
#### Scheduler
|
||||
|
||||
The Scheduler is a control plane process that assigns pods to nodes. It watches for newly created pods that have no nodes assigned. For every pod that the Scheduler discovers, the Scheduler becomes responsible for finding the best node for that pod to run on.
|
||||
|
||||
Nodes that meet the scheduling requirements for a pod get called feasible nodes. If none of the nodes are suitable, the pod remains unscheduled until the Scheduler can place it. Once it finds a feasible node, it runs a set of functions to score the nodes, and the node with the highest score gets selected. It then notifies the API server about the selected node. They call this process binding.
|
||||
|
||||
The selection of nodes is a two-step process:
|
||||
|
||||
1. Filtering the list of all nodes to obtain a list of acceptable nodes to which you can schedule the pod (for example, the PodFitsResources filter checks whether a candidate node has enough available resources to meet a pod's specific resource requests).
|
||||
2. Scoring the list of nodes obtained from the first step and ranking them to choose the best node. If multiple nodes have the highest score, a round-robin process ensures the pods get deployed across all of them evenly.
|
||||
|
||||
|
||||
|
||||
Factors to consider for scheduling decisions include:
|
||||
|
||||
* Does the pod request hardware/software resources? Is the node reporting a memory or a disk pressure condition?
|
||||
* Does the node have a label that matches the node selector in the pod specification?
|
||||
* If the pod requests binding to a specific host port, is that port available?
|
||||
* Does the pod tolerate the taints of the node?
|
||||
* Does the pod specify node affinity or anti-affinity rules?
|
||||
|
||||
|
||||
|
||||
The Scheduler doesn't instruct the selected node to run the pod. All the Scheduler does is update the pod definition through the API server. The API server then notifies the kubelet that the pod got scheduled through the watch mechanism. Then the kubelet service on the target node sees that the pod got scheduled to its node, it creates and runs the pod's containers.
|
||||
|
||||
**[ Read next: [How Kubernetes creates and runs containers: An illustrated guide][9] ]**
|
||||
|
||||
### Worker node components
|
||||
|
||||
Worker nodes run the kubelet agent, which permits them to get recruited by the control plane to process jobs. Similar to the control plane, the worker node uses several different components to make this possible. The following sections describe the worker node components.
|
||||
|
||||
#### Kubelet
|
||||
|
||||
Kubelet is an agent that runs on each node in the cluster and is responsible for everything running on a worker node. It ensures that the containers run in the pod.
|
||||
|
||||
The main functions of kubelet service are:
|
||||
|
||||
* Register the node it's running on by creating a node resource in the API server.
|
||||
* Continuously monitor the API server for pods that got scheduled to the node.
|
||||
* Start the pod's containers by using the configured container runtime.
|
||||
* Continuously monitor running containers and report their status, events, and resource consumption to the API server.
|
||||
* Run the container liveness probes, restart containers when the probes fail and terminate containers when their pod gets deleted from the API server (notifying the server about the pod termination).
|
||||
|
||||
|
||||
|
||||
#### Service proxy
|
||||
|
||||
The service proxy (kube-proxy) runs on each node and ensures that one pod can talk to another pod, one node can talk to another node, and one container can talk to another container. It is responsible for watching the API server for changes on services and pod definitions to maintain that the entire network configuration is up to date. When a service gets backed by more than one pod, the proxy performs load balancing across those pods.
|
||||
|
||||
The kube-proxy got its name because it began as an actual proxy server that used to accept connections and proxy them to the pods. The current implementation uses iptables rules to redirect packets to a randomly selected backend pod without passing them through an actual proxy server.
|
||||
|
||||
A high-level view of how it works:
|
||||
|
||||
* When you create a service, a virtual IP address gets assigned immediately.
|
||||
* The API server notifies the kube-proxy agents running on worker nodes that a new service exists.
|
||||
* Each kube-proxy makes the service addressable by setting up iptables rules, ensuring each service IP/port pair gets intercepted and the destination address gets modified to one of the pods that back the service.
|
||||
* Watches the API server for changes to services or its endpoint objects.
|
||||
|
||||
|
||||
|
||||
#### Container runtime
|
||||
|
||||
There are two categories of container runtimes:
|
||||
|
||||
* **Lower-level container runtimes:** These focus on running containers and setting up the namespace and cgroups for containers.
|
||||
* **Higher-level container runtimes (container engine):** These focus on formats, unpacking, management, sharing of images, and providing APIs for developers.
|
||||
|
||||
|
||||
|
||||
Container runtime takes care of:
|
||||
|
||||
* Pulls the required container image from an image registry if it's not available locally.
|
||||
* Extracts the image onto a copy-on-write filesystem and all the container layers overlay to create a merged filesystem.
|
||||
* Prepares a container mount point.
|
||||
* Sets the metadata from the container image like overriding CMD, ENTRYPOINT from user inputs, and sets up SECCOMP rules, ensuring the container runs as expected.
|
||||
* Alters the kernel to assign isolation like process, networking, and filesystem to this container.
|
||||
* Alerts the kernel to assign some resource limits like CPU or memory limits.
|
||||
* Pass system call (syscall) to the kernel to start the container.
|
||||
* Ensures that the SElinux/AppArmor setup is proper.
|
||||
|
||||
|
||||
|
||||
### Working together
|
||||
|
||||
System-level components work together to ensure that each part of a Kubernetes cluster can realize its purpose and perform its functions. It can sometimes be overwhelming (when you're deep into editing a [YAML file)][10] to understand how your requests get communicated within your cluster. Now that you have a map of how the pieces fit together, you can better understand what's happening inside Kubernetes, which helps you diagnose problems, maintain a healthy cluster, and optimize your own workflow.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/22/2/kubernetes-architecture
|
||||
|
||||
作者:[Nived Velayudhan][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/nivedv
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/containers_modules_networking_hardware_parts.png?itok=rPpVj92- (Parts, modules, containers for software)
|
||||
[2]: https://opensource.com/sites/default/files/uploads/kubernetes-architecture-diagram.png (Kubernetes architecture diagram)
|
||||
[3]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[4]: https://www.geeksforgeeks.org/raft-consensus-algorithm/
|
||||
[5]: https://stackoverflow.com/questions/52910322/kubernetes-resource-versioning#:~:text=Optimistic%20concurrency%20control%20(sometimes%20referred,updated%2C%20the%20version%20number%20increases.
|
||||
[6]: https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/
|
||||
[7]: https://kubernetes.io/docs/concepts/architecture/garbage-collection/
|
||||
[8]: https://kubernetes.io/docs/concepts/architecture/cloud-controller/
|
||||
[9]: https://www.redhat.com/architect/how-kubernetes-creates-runs-containers
|
||||
[10]: https://www.redhat.com/sysadmin/yaml-beginners
|
@ -2,7 +2,7 @@
|
||||
[#]: via: "https://www.opensourceforu.com/2022/06/containerisation-of-java-microservices/"
|
||||
[#]: author: "Krishna Mohan Koyya https://www.opensourceforu.com/author/krishna-mohan-koyya/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
@ -1,37 +0,0 @@
|
||||
[#]: subject: "The First Commercial Unikernel With POSIX Support"
|
||||
[#]: via: "https://www.opensourceforu.com/2022/06/the-first-commercial-unikernel-with-posix-support/"
|
||||
[#]: author: "Laveesh Kocher https://www.opensourceforu.com/author/laveesh-kocher/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
The First Commercial Unikernel With POSIX Support
|
||||
======
|
||||
![operating system][1]
|
||||
|
||||
Lynx Software Technologies has released a unikernel that it claims is the first to be POSIX compatible for real-time operation and commercially available. LynxElement will be included in the MOSA.ic range of mission-critical embedded applications. To provide more security with third-party or open source software, Lynx prefers a unikernel approach over hypervisors or virtual machines. LynxElement is based on Lynx’s commercially proven LynxOS-178 real-time operating system, which allows for compatibility between the Unikernel and the standalone LynxOS-178 product. This enables designers to move applications between environments and is compliant with the POSIX API and US FACE specifications.
|
||||
|
||||
LynxElement initially focused on security on both Intel and Arm multicore processor architectures. Running security components such as virtual private networks is a common use case (VPNs). The unikernel, by utilising a one-way software ‘data diode’ and filter, can enable a customer to replace a Linux virtual machine, saving memory space and drastically reducing the attack surface while ensuring timing requirements and safety certifiability.
|
||||
|
||||
Unikernels are best suited for applications that require speed, agility, and a small attack surface in order to increase security and certifiability, such as aircraft systems, autonomous vehicles, and critical infrastructure. These run pre-built applications with their own libraries, reducing the attack surface caused by resource sharing. This also enables the secure use of containerised applications such as Kubernetes or Docker, which are increasingly moving from enterprise to embedded designs, owing to the need to support AI frameworks.
|
||||
|
||||
Unikernels are also an excellent choice for mission-critical systems with heterogeneous workloads that require the coexistence of RTOS, Linux, Unikernel, and bare-metal guest operating systems. Existing open source unikernel implementations, according to Lynx, haven’t fared well due to a lack of adequate functionality, a lack of a clear path to safety certification, and immature toolchains for debugging and producing images.
|
||||
|
||||
Lynx created the MOSA.ic software framework for developing and integrating complex multi-core safety- or security-critical systems. The framework includes built-in security for the unikernel, allowing for security and safety certification in mission-critical applications and making it enterprise-ready. With the assistance of DESE Research, Lynx created the safety-critical Unikernel solution. LynxElement is being evaluated by existing Lynx customers as well as additional organisations around the world, including naval, air force, and army organisations.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.opensourceforu.com/2022/06/the-first-commercial-unikernel-with-posix-support/
|
||||
|
||||
作者:[Laveesh Kocher][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.opensourceforu.com/author/laveesh-kocher/
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://www.opensourceforu.com/wp-content/uploads/2022/06/operating-system-1.jpg
|
@ -0,0 +1,132 @@
|
||||
[#]: subject: "Minetest, an Open Source Minecraft Alternative"
|
||||
[#]: via: "https://itsfoss.com/minetest/"
|
||||
[#]: author: "John Paul https://itsfoss.com/author/john/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Minetest, an Open Source Minecraft Alternative
|
||||
======
|
||||
Back in 2009, Minecraft was introduced to the world. Since then, it has become a cultural phenomenon. In that time period, several developers have released open-source games with similar ideas and mechanics. Today, we will look at one of the biggest ones: Minetest.
|
||||
|
||||
### What is Minetest?
|
||||
|
||||
![minetest start][1]
|
||||
|
||||
[Minetest][2], put simply, is a voxel based sandbox game, very similar to Minecraft. Unlike Minecraft, Minetest is written in C++ and is designed to run natively on most systems. It also has a very large map area. With a map size of “62,000 × 62,000 × 62,000 blocks”, “you can mine 31,000 blocks down, or build 31,000 blocks up”.
|
||||
|
||||
Interestingly, Minetest was originally released under a proprietary license but was later relicensed as GPL. It has since been relicensed to LGPL.
|
||||
|
||||
Minetest has a couple of modes. You can either build and be creative, or you can try to survive the elements. You aren’t limited to these modes. There is a wealth of [extra content available][3] to Minetest in terms of mods, texture packs, and games built within Minetest. This is largely done using Minetest’s [modding API][4] and Lua.
|
||||
|
||||
![minetest packages][5]
|
||||
|
||||
For those who have played Minecraft, you will find a very similar experience in Minetest. You can dig for resources, build structures, and combine materials to make tools. The one thing that I have not noticed in Minetest is monsters. I don’t think there are any creatures in Minetest, but then again, I’ve on only played in creative mode. I haven’t played survival mode.
|
||||
|
||||
Minetest is also used in [education][6]. For example, the people at CERN in Switzerland created a game with Minetest to [show how the Internet works][7] and how it was created. Minetest has also been [used to teach][8] programming, earth sciences, and calculus and trigonometry.
|
||||
|
||||
![minetes map1][9]
|
||||
|
||||
### How to Install Minetest?
|
||||
|
||||
Minetest is available on almost every system. Here is a list of commands that you can use to install Minetest in some of the most popular Linux distros.
|
||||
|
||||
#### Ubuntu or Debian
|
||||
|
||||
If you have a distro based on Ubuntu or Debian, just enter this command in the terminal:
|
||||
|
||||
```
|
||||
sudo apt install mintest
|
||||
```
|
||||
|
||||
#### Arch or Manjaro
|
||||
|
||||
For systems based on Arch (such as Manjaro), use:
|
||||
|
||||
```
|
||||
sudo pacman -S minetest
|
||||
```
|
||||
|
||||
#### Fedora
|
||||
|
||||
You can install Mintest from the Fedora servers by entering:
|
||||
|
||||
```
|
||||
sudo dnf install mintest
|
||||
```
|
||||
|
||||
#### openSUSE
|
||||
|
||||
openSUSE users can install Minetest using this command:
|
||||
|
||||
```
|
||||
sudo zypper in mintest
|
||||
```
|
||||
|
||||
#### FreeBSD
|
||||
|
||||
FreeBSD users are in luck. They can install Mintest using this command:
|
||||
|
||||
```
|
||||
pkg install minetest minetest_game
|
||||
```
|
||||
|
||||
![minetest map2][10]
|
||||
|
||||
#### Snap
|
||||
|
||||
To install a Snap of Minetest, enter the following command in the terminal:
|
||||
|
||||
```
|
||||
sudo snap install minetest
|
||||
```
|
||||
|
||||
#### Flathub
|
||||
|
||||
To install, enter`:`
|
||||
|
||||
```
|
||||
flatpak install flathub net.minetest.Minetest
|
||||
```
|
||||
|
||||
You can download a portable executable for Windows [here][11]. You can also install Minetest on Android, either by going to [Google Play][12] or [download the APK][13].
|
||||
|
||||
### Final Thoughts
|
||||
|
||||
![minetest about][14]
|
||||
|
||||
I’ve spent hours in Minetest building and exploring on my local system. It’s great fun. I haven’t gotten around to trying out any of the extra content because I’ve been more than happy with the relatively small portion of the game I’ve played. The only trouble that I’ve encountered was that it ran slow on Fedora, for some reason. I might have had something configured wrong.
|
||||
|
||||
If you ever thought that Minecraft looked interesting, but didn’t want to spend the money, check out Minetest. You’ll be glad that you did.
|
||||
|
||||
If you have played Minetest, tell us how your experience was in the comments.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/minetest/
|
||||
|
||||
作者:[John Paul][a]
|
||||
选题:[lkxed][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/john/
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://itsfoss.com/wp-content/uploads/2022/03/minetest-start-800x411.jpg
|
||||
[2]: https://www.minetest.net/
|
||||
[3]: https://content.minetest.net/
|
||||
[4]: https://dev.minetest.net/Modding_Intro
|
||||
[5]: https://itsfoss.com/wp-content/uploads/2022/03/minetest-packages-800x411.jpg
|
||||
[6]: https://www.minetest.net/education/
|
||||
[7]: https://forum.minetest.net/viewtopic.php?t=22871
|
||||
[8]: https://en.wikipedia.org/wiki/Minetest#Usage_in_education
|
||||
[9]: https://itsfoss.com/wp-content/uploads/2022/03/minetes-map1-800x411.png
|
||||
[10]: https://itsfoss.com/wp-content/uploads/2022/03/minetest-map2-800x413.png
|
||||
[11]: https://www.minetest.net/downloads/
|
||||
[12]: https://play.google.com/store/apps/details?id=net.minetest.minetest&utm_source=website&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1
|
||||
[13]: https://github.com/minetest/minetest/releases/download/5.5.0/app-armeabi-v7a-release.apk
|
||||
[14]: https://itsfoss.com/wp-content/uploads/2022/03/minetest-about-800x407.jpg
|
@ -0,0 +1,70 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (Donkey)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (3 stress-free steps to tackling your task list)
|
||||
[#]: via: (https://opensource.com/article/21/1/break-down-tasks)
|
||||
[#]: author: (Kevin Sonney https://opensource.com/users/ksonney)
|
||||
|
||||
轻松解决你的任务清单的三个步骤
|
||||
======
|
||||
将你的大任务分为小步骤,避免自己不堪重负。
|
||||
![Team checklist][1]
|
||||
|
||||
去年,这个年度系列文章覆盖了个人应用。今年,除了在 2021 年提供帮助的策略外,我们还在寻找一体化解决方案。欢迎来到 2021 年 21 天生产力的第 14 天。
|
||||
|
||||
本周开始,我先回顾我的日程安排,看看我需要或想要完成的事情。通常,列表上有些较大的项目。无论来自工作上的问题,一系列关于生产力的文章,或者改进鸡舍,当作为一项工作时,这个任务真的很艰巨。很有可能在一个时间段我不能坐下来,甚至在一天内完成类似(例如,请注意)21 篇文章之类的事情。
|
||||
|
||||
![21 Days of Productivity project screenshot][2]
|
||||
|
||||
21 天的生产力 (Kevin Sonney, [CC BY-SA 4.0][3])
|
||||
|
||||
所以当我的清单上有这样的东西时,我做的第一件事就是把它分解成更小的部分。如著名的诺贝尔文学奖得主 [William Faulkner][4] 说的“移山的人,从小石头开始。”(译注:感觉与“千里之行,始于足下”是一个意思) 我们要解决大任务(山)并且需要完成各个步骤(小石头)。
|
||||
|
||||
|
||||
我使用下面的步骤将大任务分割为小步骤:
|
||||
|
||||
1. 我通常很清楚完成一项任务需要做什么。 如果没有,我会做一些研究来弄清楚这一点。
|
||||
2. 我会顺序的写下完成的步骤。
|
||||
3. 最后,我坐下来拿着我的日历和清单,开始将任务分散到几天(或几周或几个月),以了解我何时可以完成它。
|
||||
|
||||
|
||||
现在我不仅有计划还知道多久能完成。逐步完成,我可以看到这项大任务不仅变得更小,而且更接近完成。
|
||||
|
||||
军队有句古话,“遇敌无计”。 几乎可以肯定的是,有一两点(或五点)我意识到像“截屏”这样简单的事情需要扩展到更复杂的事情。 事实上,在 [Easy!Appointments][5] 的截图中,竟然是:
|
||||
|
||||
1. 安装和配置 Easy!Appointments
|
||||
2. 安装和配置 Easy!Appointments WordPress 插件
|
||||
3. 生成 API 密钥来同步日历
|
||||
4. 截屏
|
||||
|
||||
|
||||
|
||||
即便如此,我也不得不将这些任务分解成更小的部分——下载软件、配置 NGINX、验证安装……你明白了。 没关系。 一个计划或一组任务不是一成不变的,可以根据需要进行更改。
|
||||
|
||||
![project completion pie chart][6]
|
||||
|
||||
今年的计划已经完成了 2/3 ! (Kevin Sonney, [CC BY-SA 4.0][3])
|
||||
|
||||
这是一项后天习得的技能,最初几次需要一些努力。学习如何将大任务分解成更小的步骤可以让您跟踪实现目标或完成大任务的进度,而不会在过程中不知所措。
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/1/break-down-tasks
|
||||
|
||||
作者:[Kevin Sonney][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[Donkey](https://github.com/Donkey-Hao)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/ksonney
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/checklist_todo_clock_time_team.png?itok=1z528Q0y (Team checklist)
|
||||
[2]: https://opensource.com/sites/default/files/day14-image1.png
|
||||
[3]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[4]: https://en.wikipedia.org/wiki/William_Faulkner
|
||||
[5]: https://opensource.com/article/21/1/open-source-scheduler
|
||||
[6]: https://opensource.com/sites/default/files/day14-image2_1.png
|
@ -0,0 +1,197 @@
|
||||
[#]: subject: (9 reasons I love to use the Qt Creator IDE)
|
||||
[#]: via: (https://opensource.com/article/21/6/qtcreator)
|
||||
[#]: author: (Stephan Avenwedde https://opensource.com/users/hansic99)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (hadisi1993)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
|
||||
9个我爱用Qt Creator IDE的原因
|
||||
======
|
||||
Qt Creator 就是丰富的Qt库和程序员之间的胶水。
|
||||
![坐在窗前用笔记本电脑的商务女性][1]
|
||||
|
||||
Qt Creator 是Ot框架默认的集成开发环境(IDE),同时也是丰富的Qt库和用户之前的胶水。除了如智能代码补全,调试,项目管理等基础功能外,Qt Creator还提供了很多让软件开发变得更简单的特性。
|
||||
|
||||
在这篇文章中,我会重点介绍一些我最喜欢的[Qt Creator][2]特性。
|
||||
### 黑暗模式
|
||||
|
||||
当我使用一个新的应用时,我的第一个问题是:_这里有黑暗模式吗?_ Qt Creator的回答是:_你更喜欢哪一种黑暗模式呢?_
|
||||
|
||||
你可以在选项菜单中激活黑暗模式。在顶部的菜单栏中,点击**工具**,选择**选项**,然后转到**环境**部分。下面是你能选择的常用外观:
|
||||
|
||||
![ QT Creator 黑暗模式][3]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
### 定制外观
|
||||
|
||||
像每一个Qt应用一样,借助样式表,Qt Creator的外观是高度可定制化的。下面,你可以按照我的做法给Qt Creator一个想要的外观。
|
||||
|
||||
将下面这些内容写入`mycustomstylesheet.css`文件中:
|
||||
|
||||
```
|
||||
QMenuBar { background-color: olive }
|
||||
QMenuBar::item { background-color: olive }
|
||||
QMenu { background-color : beige; color : black }
|
||||
QLabel { color: green }
|
||||
```
|
||||
|
||||
然后使用命令行开启Qt Creator,将样式表作为参数传入:
|
||||
|
||||
|
||||
```
|
||||
`qtcreator -stylesheet=mycustomstylesheet.css`
|
||||
```
|
||||
|
||||
IDE现在看上去应该会变成这样:
|
||||
|
||||
![QT Creator 定制样式表][5]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
在这份[文档][6]中可以查阅更多的样式表
|
||||
|
||||
### 命令行参数
|
||||
Qt Creator 可接受很多命令行选项。例如,如果想在启动时自动加载当前项目,那么你可以将它的路径传递给`*.pro-file`:
|
||||
|
||||
```
|
||||
`qtcreator ~/MyProject/MyQtProject.pro`
|
||||
```
|
||||
|
||||
你甚至可以将默认应该打开的文件和行数作为参数传递。下面这个命令在20行处打开`main.cpp`:
|
||||
|
||||
```
|
||||
`qtcreator ~/MyProject/main.cpp:20`
|
||||
```
|
||||
|
||||
在这份[文档][7]中可以查阅更多Qt特有的命令行选项。
|
||||
|
||||
|
||||
Qt Creator和一般的Qt应用无二,所以,除了自己的命令行参数以外,它也接收[QApplication][8]和[QGuiApplication][9]的一般参数。
|
||||
### 交叉编译
|
||||
|
||||
Qt Creator allows you to define several toolchains, called **Kits**. A kit defines the binaries and SDK for building and running an application:
|
||||
Qt Creator允许你定义一些被称为**Kits**的toolchains。一个Kit定义构建和运行应用所需要的二进制库和SDK。
|
||||
![QT Creator kits][10]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
This allows you to switch between completely different toolchains with just two clicks:
|
||||
这使得你通过两次点击,就在完全不同的toolchains之间切换。
|
||||
|
||||
![在Qt Creator中切换Kits][11]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
在这份[手册][12]中可以查阅更多关于Kits的内容。
|
||||
### 分析工具
|
||||
|
||||
Qt Creator集成了一些最流行的性能分析工具,例如:
|
||||
|
||||
* [Linux Performance Analyzer][13] (需要特定的内核)
|
||||
* [Valgrind][14] memory profiler
|
||||
* [Clang-Tidy and Clazy][15], 一种检查C/C++的linter
|
||||
|
||||
|
||||
![Qt Creator性能分析工具][16]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
### 调试器
|
||||
|
||||
在调试方面,Qt Creator为GNU Debugger(GDB)配备了一个很好的界面。我喜欢它检查容器类型和创建条件断点的方式,很简易。
|
||||
|
||||
![Qt Creator 调试器][17]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
### FakeVim
|
||||
|
||||
如果你喜欢Vim,你可以开启在设置中开启FakeVim来像Vim一样控制Qt Creator。点击**工具**并选择**选项**。在**FakeVim**选项中,你可以找到许多开关来定制FakeVim。除了编辑器的功能外,你可以将自己设置的功能和命令关联起来,定制Vim命令。
|
||||
|
||||
举个例子,你可以将**创建项目**的功能和`build`命令关联到一起去:
|
||||
For example, you can map the function **Build Project** to the `build` command:
|
||||
|
||||
![Qt Creator中的FakeVim][18]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
回到编辑器中,当你按下冒号并输入build,Qt Creator利用配置的toolchain,开始进行构建:
|
||||
|
||||
![Qt Creator中的FakeVim][19]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
你可以中这份[文档][20]中找到FakeVim的更多信息。
|
||||
### 类检测器
|
||||
|
||||
当使用C++开发时,点击Qt Creator右下角的按钮可打开右边的窗口。然后在窗口顶部拉下的菜单中选择**轮廓**。如果你在左侧窗体中有头文件打开,你可以很好地纵览定义的类和类型。如果你切换到源文件中(`*.cpp`),右侧窗体会列出所有定义的方法,双击其中一个,你可以跳转到这个方法:
|
||||
![Qt Creator中的类列表][21]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
### 项目配置
|
||||
|
||||
Qt Creator 的项目建立在项目文件里的`*.pro-file`之上。你可以为你的项目在`*.pro-file`中添加你定制的配置。我向`*.pro-file`中添加了`my_special_config`,它向编译器的定义添加`MY_SPECIAL_CONFIG`。
|
||||
|
||||
```
|
||||
QT -= gui
|
||||
|
||||
CONFIG += c++11 console
|
||||
CONFIG -= app_bundle
|
||||
|
||||
CONFIG += my_special_config
|
||||
|
||||
my_special_config {
|
||||
DEFINES += MY_SPECIAL_CONFIG
|
||||
}
|
||||
```
|
||||
|
||||
Qt Creator 自动根据当前配置设置代码高亮:
|
||||
![Qt Creator的特殊配置][22]
|
||||
|
||||
(Stephan Avenwedde, [CC BY-SA 4.0][4])
|
||||
|
||||
`*.pro-file` 使用[qmake语言][23]进行编写
|
||||
### Summary
|
||||
这些特性仅仅是Qt Creators提供特性的冰山一角。初学者们应该不会感到被其众多的功能所淹没,Qt Creator是一款对初学者很友好的IDE。它甚至可能是开始C++开发最简单的方式。如果要获得QT Creator特性的全面概述,请参考它的[官方文档][24]。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/6/qtcreator
|
||||
|
||||
作者:[Stephan Avenwedde][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[hadisi1993](https://github.com/hadisi1993)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/hansic99
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/lenovo-thinkpad-laptop-concentration-focus-windows-office.png?itok=-8E2ihcF (Woman using laptop concentrating)
|
||||
[2]: https://www.qt.io/product/development-tools
|
||||
[3]: https://opensource.com/sites/default/files/uploads/qt_creator_dark_mode.png ( QT Creator dark mode)
|
||||
[4]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[5]: https://opensource.com/sites/default/files/uploads/qt_creator_custom_stylesheet2.png (QT Creator custom stylesheet)
|
||||
[6]: https://doc.qt.io/qt-5/stylesheet-reference.html
|
||||
[7]: https://doc.qt.io/qtcreator/creator-cli.html
|
||||
[8]: https://doc.qt.io/qt-5/qapplication.html#QApplication
|
||||
[9]: https://doc.qt.io/qt-5/qguiapplication.html#supported-command-line-options
|
||||
[10]: https://opensource.com/sites/default/files/uploads/qt_creator_cross_compiling.png (QT Creator kits)
|
||||
[11]: https://opensource.com/sites/default/files/uploads/qt_creator_select_kits.png (Switching between Kits in Qt Creator)
|
||||
[12]: https://doc.qt.io/qtcreator/creator-targets.html
|
||||
[13]: https://doc.qt.io/qtcreator/creator-cpu-usage-analyzer.html
|
||||
[14]: https://doc.qt.io/qtcreator/creator-valgrind-overview.html
|
||||
[15]: https://doc.qt.io/qtcreator/creator-clang-tools.html
|
||||
[16]: https://opensource.com/sites/default/files/uploads/qt_creator_analyzer.png (Qt Creator analyzer)
|
||||
[17]: https://opensource.com/sites/default/files/uploads/qt_creator_debugger2.png (Qt Creator debugger)
|
||||
[18]: https://opensource.com/sites/default/files/uploads/qt_creator_fakevim_ex_commands.png (FakeVim in Qt Creator)
|
||||
[19]: https://opensource.com/sites/default/files/uploads/qt_creator_fakevim_build_commands.png (FakeVim in Qt Creator)
|
||||
[20]: https://doc.qt.io/qtcreator/creator-editor-fakevim.html
|
||||
[21]: https://opensource.com/sites/default/files/uploads/qtcreator_class_overview.png (List of classes in Qt Creator)
|
||||
[22]: https://opensource.com/sites/default/files/uploads/qtcreater_special_config.png (Special configuration in Qt Creator)
|
||||
[23]: https://doc.qt.io/qt-5/qmake-language.html
|
||||
[24]: https://doc.qt.io/qtcreator/
|
188
translated/tech/20220214 A guide to Kubernetes architecture.md
Normal file
188
translated/tech/20220214 A guide to Kubernetes architecture.md
Normal file
@ -0,0 +1,188 @@
|
||||
[#]: subject: "A guide to Kubernetes architecture"
|
||||
[#]: via: "https://opensource.com/article/22/2/kubernetes-architecture"
|
||||
[#]: author: "Nived Velayudhan https://opensource.com/users/nivedv"
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: "MjSeven"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Kubernetes 架构指南
|
||||
======
|
||||
学习 Kubernetes 架构的不同组件是如何组合在一起的,这样你就可以更好地诊断问题、维护健康的集群和优化你的工作流。
|
||||
![部件、模块、软件容器][1]
|
||||
|
||||
使用 Kubernetes 来编排容器,这是一个简单的描述,但理解它的实际含义和你如何实现它完全是另外一回事。如果你正在运行或管理 Kubernetes 集群,那么你就会知道 Kubernetes 由一台称为 _控制平面_ 的计算机和许多其他 _工作节点_ 计算机组成。每一个都有一个复杂但健壮的堆栈,这使编排成为可能,熟悉每个组件有助于理解它是如何工作的。
|
||||
|
||||
![Kubernetes 架构图][2]
|
||||
|
||||
(Nived Velayudhan, [CC BY-SA 4.0][3])
|
||||
|
||||
### 控制平面组件
|
||||
|
||||
Kubernetes 安装在一个称为控制平面的机器上,它会运行 Kubernetes 守护进程,并在启动容器和吊舱时与之通信。下面介绍控制平面的各个组件。
|
||||
|
||||
#### Etcd
|
||||
|
||||
Etcd 是一种快速、分布式且一致的键值存储器,用作持久存储 Kubernetes 对象数据,如吊舱、Replication Controller、密钥和服务的后台存储。Etcd 是 Kubernetes 存储集群状态和元数据的唯一地方。唯一直接与 etcd 对话的组件是 Kubernetes API 服务器。所有的其他组件都通过 API 服务器间接的从 etcd 读写数据。
|
||||
|
||||
Etcd 还实现了一个监控功能,它提供了一个基于事件的接口,用于异步监控密钥的更改。一旦你更改了一个密钥,它的监控者就会收到通知。API 服务器组件严重依赖于此来获得通知,并将 etcd 移动到所需状态。
|
||||
|
||||
_为什么 etcd 实例的数量应该是奇数?_
|
||||
|
||||
你通常会在高可用(HA)环境中运行三个、五个或七个 etcd 实例,但这是为什么呢?因为 etcd 是分布式数据存储,可以水平扩展它,但你需要确保每个实例中的数据是一致的。为此,系统需要当前状态是什么达成共识,Etcd 为此使用 [RAFT 共识算法][4]。
|
||||
|
||||
RAFT 算法需要多数(或仲裁)集群才能进入下一个状态。如果你只有两个 etcd 实例并且他们其中一个失败的话,那么 etcd 集群无法转换到新的状态,因为不存在多数这个概念。如果你有三个 etcd 实例,一个实例可能会失败,但仍有 2 个实例可用于达到仲裁。
|
||||
|
||||
#### API 服务器
|
||||
|
||||
API 服务器是 Kubernetes 中唯一直接与 etcd 交互的组件。Kubernetes 中的其他所有组件都必须通过 API 服务器来处理集群状态,包括客户端(kubectl)。API 服务器具有以下功能:
|
||||
|
||||
* 提供在 etcd 中存储对象的一致方式。
|
||||
* 对对象执行验证,方便客户端无法存储配置不正确的对象(如果它们直接写入 etcd 数据存储,可能会发生这种情况)。
|
||||
* 提供 RESTful API 来创建、更新、修改或删除资源。
|
||||
* 提供[乐观并发锁][5],在发生更新时,其他客户端永远不会有机会重写对象。
|
||||
* 对客户端发送的请求进行身份验证和授权。它使用插件提取客户端的用户名、ID、所属组,并确定通过身份验证的用户是否可以对请求的资源执行请求的操作。
|
||||
* 如果请求试图创建、修改或删除资源,则负责[权限控制][6]。例如,AlwaysPullImages、DefaultStorageClass 和 ResourceQuota。
|
||||
* 实现了一种监控机制(类似于 etcd),用户客户端监控更改。这允许调度器和控制器管理器等组件以松耦合的方式与 API 服务器交互。
|
||||
|
||||
#### 控制器管理器
|
||||
|
||||
在 Kubernetes 中,控制器是监控集群状态的控制循环,然后根据需要进行或请求更改。每个控制器都尝试将当前集群状态移动到所需状态。控制器至少跟踪一种 Kubernetes 资源类型,这些对象都有一个字段来表示所需的状态。
|
||||
|
||||
控制器示例:
|
||||
|
||||
* Replication Manager(ReplicationController 资源的控制器)
|
||||
* 复本控制器、DaemonSet 和 Job 控制器
|
||||
* 部署控制器
|
||||
* StatefulSet 控制器
|
||||
* 节点控制器
|
||||
* 服务控制器
|
||||
* 端点控制器
|
||||
* 命名空间控制器
|
||||
* 持久卷控制器
|
||||
|
||||
|
||||
控制器使用监控机制来获得更改通知。它们监视 API 服务器对资源的更改,对每次更改执行操作,无论是新建对象还是更新或删除现有对象。大多数时候,这些操作包括创建其他资源或更新监控的资源本身。不过,由于使用监控并不能保证控制器不会错过任何事件,它们还会定期执行一系列操作,确保没有错过任何事件。
|
||||
|
||||
控制器管理器还执行生命周期功能。例如命名空间创建和生命周期、事件垃圾收集、终止吊舱垃圾收集、[级联删除垃圾收集][7]和节点垃圾收集。有关更多信息,参考[云控制器管理器][8]。
|
||||
|
||||
#### 调度器
|
||||
|
||||
调度器是一个将吊舱分配给节点的控制平面进程。它会监视新创建没有分配节点的吊舱。调度器会给每个发现的吊舱分配运行它的最佳节点。
|
||||
|
||||
满足吊舱调度要求的节点称为可行节点。如果没有合适的节点,那么吊舱会一直处于未调度状态,直到调度器可以放置它。一旦找到可行节点,它就会运行一组函数来对节点进行评分,并选择得分最高的节点,然后它会告诉 API 服务器所选节点的信息。这个过程称为绑定。
|
||||
|
||||
节点的选择分为两步:
|
||||
|
||||
1. 过滤所有节点的列表,获得可以调度吊舱的可接受节点列表(例如,PodFitsResources 过滤器检查候选节点是否有足够的可用资源来满足吊舱的特定资源请求)。
|
||||
|
||||
2. 对第一步得到的节点列表进行评分和排序,选择最佳节点。如果得分最高的有多个节点,循环过程可确保吊舱会均匀地部署在所有节点上。
|
||||
|
||||
调度决策要考虑的因素包括:
|
||||
|
||||
* 吊舱是否请求硬件/软件资源?节点是否报告内存或磁盘压力情况?
|
||||
|
||||
* 节点是否有与吊舱规范中的节点选择器匹配的标签?
|
||||
|
||||
* 如果吊舱请求绑定到特定地主机端口,该端口是否可用?
|
||||
|
||||
* 吊舱是否容忍节点的污点?
|
||||
|
||||
* 吊舱是否指定节点亲和性或反亲和性规则?
|
||||
|
||||
|
||||
调度器不会指示所选节点运行吊舱。调度器所做的就是通过 API 服务器更新吊舱定义。然后 API 服务器通过监控机制通知 kubelet 吊舱已被调度,然后目标节点上的 kubelet 服务看到吊舱被调度到它的节点,它创建并运行吊舱的容器。
|
||||
|
||||
**[ 下一篇: [Kubernetes 如何创建和运行容器: 图解指南][9] ]**
|
||||
|
||||
### 工作节点组件
|
||||
|
||||
工作节点运行 kubelet 代理,这允许控制平面招募它们来处理作业。与控制平面类似,工作节点使用几个不同的组件来实现这一点。 以下部分描述了工作节点组件。
|
||||
|
||||
#### Kubelet
|
||||
|
||||
Kubelet 是一个运行在集群中每个节点上的代理,负责在工作节点上运行的所有事情。它确保容器在吊舱中运行。
|
||||
|
||||
kubelet服务的主要功能有:
|
||||
|
||||
* 通过在 API 服务器中创建节点资源来注册它正在运行的节点。
|
||||
|
||||
* 持续监控 API 服务器上调度到节点的吊舱。
|
||||
|
||||
* 使用配置的容器运行时启动吊舱的容器。
|
||||
|
||||
* 持续监控正在运行的容器,并将其状态、事件和资源消耗报告给 API 服务器。
|
||||
|
||||
* 运行容器存活探测,在探测失败时重启容器,当 API 服务器中删除吊舱时终止(通知服务器吊舱终止的消息)。
|
||||
|
||||
#### 服务代理
|
||||
|
||||
服务代理(kube-proxy)在每个节点上运行,确保一个吊舱可以与另一个吊舱对话,一个节点可以与另一个节点对话,一个容器可以与另一个容器对话。它负责监视 API 服务器对服务和吊舱定义的更改,以保持整个网络配置是最新的。当一项服务得到多个吊舱的支持时,代理会在这些吊舱之间执行负载平衡。
|
||||
|
||||
kube-proxy 之所以叫代理,是因为它最初实际上是一个代理服务器,用于接受连接并将它们代理到吊舱。当前的实现是使用 iptables 规则将数据包重定向到随机选择的后端吊舱,而无需通过实际的代理服务器。
|
||||
|
||||
它工作原理的高级视图:
|
||||
|
||||
* 当你创建一个服务时,会立即分配一个虚拟 IP 地址。
|
||||
|
||||
* API 服务器会通知在工作节点上运行的 kube-proxy 代理有一个新服务。
|
||||
|
||||
* 每个 kube-proxy 通过设置 iptables 规则使服务可寻址,确保截获每个服务 IP/端口对,并将目的地址修改为支持服务的一个吊舱。
|
||||
|
||||
* 监控 API 服务器对服务或其端点对象的更改。
|
||||
|
||||
#### 容器运行时
|
||||
|
||||
容器运行时有两类:
|
||||
|
||||
* **较低级别的容器运行时:** 它们主要关注运行中的容器并为容器设置命名空间和 cgroup。
|
||||
|
||||
* **更高级别的容器运行时(容器引擎):**它们专注于格式、解包、管理、共享镜像以及为开发人员提供 API。
|
||||
|
||||
容器运行时负责:
|
||||
|
||||
* 如果容器镜像本地没有,则从镜像仓库中提取。
|
||||
|
||||
* 将镜像解压到写时复制文件系统,所有容器层叠加创建一个合并的文件系统。
|
||||
|
||||
* 准备一个容器挂载点。
|
||||
|
||||
* 设置容器镜像的元数据,如覆盖命令、用户输入的入口命令,并设置 SECCOMP 规则,确保容器按预期运行。
|
||||
|
||||
* 提醒内核将进程、网络和文件系统等隔离分配给容器。
|
||||
|
||||
* 提醒内核分配一些资源限制,如 CPU 或内存限制。
|
||||
|
||||
* 将系统调用(syscall)传递给内核启动容器。
|
||||
|
||||
* 确保 SElinux/AppArmor 设置正确。
|
||||
|
||||
|
||||
### 协同
|
||||
|
||||
系统级组件协同工作,确保 Kubernetes 集群的每个部分都能实现其目和执行其功能。当你深入编辑 [YAML 文件][10]时,有时很难理解请求是如何在集群中通信的。现在你已经了解了各个部分是如何组合在一起的,你可以更好地理解 Kubernetes 内部发生了什么,这有助于诊断问题、维护健康的集群并优化你的工作流。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/22/2/kubernetes-architecture
|
||||
|
||||
作者:[Nived Velayudhan][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/nivedv
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/containers_modules_networking_hardware_parts.png?itok=rPpVj92- (Parts, modules, containers for software)
|
||||
[2]: https://opensource.com/sites/default/files/uploads/kubernetes-architecture-diagram.png (Kubernetes architecture diagram)
|
||||
[3]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[4]: https://www.geeksforgeeks.org/raft-consensus-algorithm/
|
||||
[5]: https://stackoverflow.com/questions/52910322/kubernetes-resource-versioning#:~:text=Optimistic%20concurrency%20control%20(sometimes%20referred,updated%2C%20the%20version%20number%20increases.
|
||||
[6]: https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/
|
||||
[7]: https://kubernetes.io/docs/concepts/architecture/garbage-collection/
|
||||
[8]: https://kubernetes.io/docs/concepts/architecture/cloud-controller/
|
||||
[9]: https://www.redhat.com/architect/how-kubernetes-creates-runs-containers
|
||||
[10]: https://www.redhat.com/sysadmin/yaml-beginners
|
Loading…
Reference in New Issue
Block a user