Ming's blog

一個軟體工程師的旅程 :)

這個網站之前因為 Cloud9 賣給 Amazon 關閉免費方案後移轉到 RedHat Codenvy,Codenvy 不像是 Cloud9 在一段時間沒使用會將工作區的狀態存起來然後暫停,而是會直接刪掉整個工作區,所以整個 Hexo 的專案檔案就這樣被砍掉(還好有 2019 年初的備份)。

中間有一些文章是暫時放在 Ming’s Blog x Notion 中。

後來去讀研究所,一直沒有時間恢復,也一直想恢復,直到今天臨時想到才把網站恢復,一晃眼就是 5 年的時間過去。

比較早期放在 Google 協作平台作為圖床的圖片也已經破圖(舊的 Google 協作平台 2023 年已經強制關閉)。幸好 Googl 有把備份 Archived Classic Site 放到 Google Drive 去,當初的圖片也都還在,再找時間放到 Backblaze B2 或者 Cloudflare R2 去!

這次趁機會把 Hexo 、hexo-theme-next 升級到最新版,整個 Hexo 的工作區的檔案也上 GitHub,預計搭配 GitHub Actions 做基本的 Containerization 然後 CD 部署到自己的 Ubutnu Server。

國中開始寫 Blog 到現在,從最一開始的 WordPress 一路到 Octopress 再到 Hexo(那時後有一段時間很流行 Static Site Generators),一晃眼 10 幾年的時間,希望還有時間、熱情加減寫一些文章 :)

早上 Grafana dashboard 的 session 過期提示我重新登入

然後發現 .. 我忘記 grafana dashboard 的密碼了 Orz


我的 grafana-server 版本是:

1
2
~$ grafana-server -v
Version 6.2.5 (commit: 6082d19, branch: HEAD)

解決方法

1
2
3
~$ sudo sqlite3 /var/lib/grafana/grafana.db
~$ update user set password = '59acf18b94d7eb0694c61e60ce44c110c7a683ac6a8f09580d626f90f4a242000746579358d77dd9e570e83fa24faa88a8a6', salt = 'F3FAxVm33R' where login = 'username';
~$ .exit

這樣就可以重置 username 的 password

還蠻 .. 暴力的 XD

Reference

最近工作上需要在 Redis 上面寫 Script,Redis 從 2.6.0 版之後包了 Lua interpreter 進去開始支援用 Lua 語言寫 Script, 所以花一些時間去熟悉久仰的 Lua 語言。

筆記一下在 Ubuntu 18.04 上手 Lua 的過程。

Install

Ubuntu 18.04 可以安裝到最新的 Lua 版本是 Lua 5.3

1
2
3
4
~$ sudo apt install lua5.3
~$ lua5.3
Lua 5.3.3 Copyright (C) 1994-2016 Lua.org, PUC-Rio
>

不過要注意的是 Redis 中的 EVAL 版本是 5.1[1]

Building from the source

~$ sudo apt install libreadline-dev
~$ git clone https://github.com/lua/lua
~$ cd ./lua
~$ make all
~$ mv ./lua ./lua /usr/local/bin
~$ lua
Lua 5.4.0 Copyright (C) 1994-2019 Lua.org, PUC-Rio

Example: Hello, world!

Interactive Mode

1
2
3
4
5
6
~$ lua
lua
Lua 5.4.0 Copyright (C) 1994-2019 Lua.org, PUC-Rio
> print("Hello, world!")
Hello, world!
>

helloworld.lua

1
~$ vim helloworld.lua
1
2
3
4
5
6
function helloWorld(name)
assert(type(name) == "string", "name expects a string")
return string.format("Hello, %s!", name)
end

print(helloWorld("world"))
1
2
~$ lua helloworld.lua
Hello, world!

Unit Test

要在 Lua 寫 Unit Test 的話,根據 Unit Testing - lua-users wiki 的範例是使用 bluebird75/luaunit

安裝 luaunit module 以 Lua 5.4 為例

1
2
3
4
~$ lua -v | awk '{print $2}'
5.4.0
~$ sudo mkdir -p /usr/local/share/lua/5.4/luaunit/
~$ sudo curl -o /usr/local/share/lua/5.4/luaunit/init.lua https://raw.githubusercontent.com/bluebird75/luaunit/master/luaunit.lua

安裝 luaunit module 以 5.3 為例

1
2
3
4
lua5.3 -v | awk '{print $2}'
5.3.3
~$ sudo mkdir -p /usr/share/lua/5.3
~$ sudo curl -o /usr/share/lua/5.3/luaunit.lua https://raw.githubusercontent.com/bluebird75/luaunit/master/luaunit.lua

Example: helloWorld module

helloworld.lua

1
2
3
4
5
6
7
8
module = {}

function module.helloWorld(name)
assert(type(name) == "string", "name expects a string")
return string.format("Hello, %s!", name)
end

return module

test_helloworld.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
luaUnit = require("luaunit")
testModule = require("helloworld")

function testHelloWorld()
local ret = testModule.helloWorld("world")
luaUnit.assertEquals(type(ret), "string")
luaUnit.assertEquals(ret, "Hello, world!")

ret = testModule.helloWorld("ming")
luaUnit.assertEquals(type(ret), "string")
luaUnit.assertEquals(ret, "Hello, ming!")

ret = testModule.helloWorld(integer)
luaUnit.assertEquals(type(ret), "string")
luaUnit.assertEquals(ret, "Hello, ming!")
end

os.exit(luaUnit.LuaUnit.run())

Run LuaUnit

1
2
3
4
5
6
~$ lua test_helloworld.lua -v
Started on Mon Jul 8 15:06:09 2019
testHelloWorld ... Ok
=========================================================
Ran 1 tests in 0.000 seconds, 1 success, 0 failures
OK

Error Handling

helloworld.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module = {}

local function helloWorld(name)
-- assert(type(name) == "string", {message="name expects a string"})
if type(name) ~= "string" then
error({message="name expects a string"})
end

return string.format("Hello, %s!", name)
end

function module.HelloWorld(name)
-- Error Handling
-- Reference: https://blog.golang.org/error-handling-and-go
local success, result = pcall(helloWorld, name)
if not success then
return "", result.message
end

return result, nil
end

return module

test_helloworld.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
luaUnit = require("luaunit")
testModule = require("helloworld")

function testHelloWorld()
local ret, err = testModule.HelloWorld("world")
luaUnit.assertNil(err)
luaUnit.assertEquals(type(ret), "string")
luaUnit.assertEquals(ret, "Hello, world!")

ret, err = testModule.HelloWorld("ming")
luaUnit.assertNil(err)
luaUnit.assertEquals(type(ret), "string")
luaUnit.assertEquals(ret, "Hello, ming!")
end

function testHelloWorldWrongCase()
local ret, err = testModule.HelloWorld(123)
luaUnit.assertNotNil(err)
luaUnit.assertEquals(ret, "")
luaUnit.assertEquals(err, "name expects a string")
end

os.exit(luaUnit.LuaUnit.run())
1
2
3
4
5
6
7
~$ lua test_helloworld.lua -v
Started on Mon Jul 8 15:42:30 2019
testHelloWorld ... Ok
testHelloWorldWrongCase ... Ok
=========================================================
Ran 2 tests in 0.000 seconds, 2 successes, 0 failures
OK

Remark

  1. “EVAL is a Lua 5.1 script.” - redis.io

Reference & Resource

  • Programming in Lua
  • lua-users wiki
  • Lua Tutorial - tutorialspoint
  • Learn Lua in 15 Minutes
    Lua on Ubuntu18.04 | Ming’s blog

學習 MicroK8s (a lightweight Kubernetes distribution) 的筆記。

Read more »

1
2
3
4
5
version=$(go version)
regex="([0-9]{1,2}.[0-9]{1,2}.[0-9]{1,2})"
if [[ $version =~ $regex ]]; then
echo ${BASH_REMATCH[1]}
fi
1
2
3
4
5
6
7
8
9
10
function funcGoVersion {
version=$(go version)
regex="([0-9]{1,2}.[0-9]{1,2}.[0-9]{1,2})"
if [[ $version =~ $regex ]]; then
echo ${BASH_REMATCH[1]}
fi
}

goVersion=$(funcGoVersion)
echo $goVersion

Reference

PPTP mobtitude/vpn-pptp

1
~$ touch chapsecrets
1
2
3
# Secrets for authentication using PAP
# client server secret acceptable local IP addresses
username * password *
1
2
~$ sudo docker pull mobtitude/vpn-pptp
~$ sudo docker run --net=host --name pptp-vpn-server -d --privileged -p 1723:1723 -v /home/chapsecrets:/etc/ppp/chap-secrets mobtitude/vpn-pptp

L2TP/IPSec PSK hwdsl2/ipsec-vpn-server

1
touch vpn.env

vpn.env reference: https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/vpn.env.example

1
2
3
~$ sudo modprobe af_key
~$ sudo docker pull hwdsl2/ipsec-vpn-server
~$ sudo docker run --name ipsec-vpn-server --env-file ./vpn.env --restart=always -p 500:500/udp -p 4500:4500/udp -v /lib/modules:/lib/modules:ro -d --privileged hwdsl2/ipsec-vpn-server

Reference

1
~$ vim setup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env python3

#from distutils.core import setup
import platform
import setuptools

def build_params():
params = {
'name':'copycat-clipboard3',
'version':'1.1',
'description':'easy way let use clip on command line with system clip (support python 3)',
'author':'Ming',
'author_email':'[email protected]',
'url':'https://github.com/iwdmb/copycat',
'py_modules':['copycat3'],
'license':'MIT',
'install_requires': ['clime', 'pyclip-copycat'],
}
if platform.system() == 'Windows':
params['scripts'] = ['copycat3.bat']
else:
params['scripts'] = ['copycat3']

return params

setuptools.setup (
**build_params()
)
1
~$ vim ~/.pypirc
1
2
3
4
5
6
7
8
[distutils]
index-servers =
pypi

[pypi]
repository=https://pypi.python.org/pypi
username=*username*
password=*password*
1
2
3
4
5
6
~$ python3 -m venv ./venv
~$ source ./venv/bin/active
~$ pip install --upgrade pip
~$ pip install --upgrade setuptools wheel
~$ python3 setup.py sdist bdist_wheel
~$ python setup.py sdist upload -r pypi

Reference

今天把系統從 Kubuntu 換成 KDE neon User Edition 並裝完 gcin 之後,發現 gcin 不能在 Konsole 中輸入中文。

解決方法很簡單也很困難,簡單之處在於安裝 gcin-qt5-immodule,難也難在安裝 gcin-qt5-immodule。

1
~$ sudo apt-get install gcin-qt5-immodule

輸入之後,會出現錯誤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
~$ sudo apt-get install gcin-qt5-immodule
Reading package lists... Done
Building dependency tree
Reading state information... Done
Starting pkgProblemResolver with broken count: 1
Starting 2 pkgProblemResolver with broken count: 1
Investigating (0) gcin-qt5-immodule [ amd64 ] < 2.8.6+eliu-0 > ( utils )
Broken gcin-qt5-immodule:amd64 Depends on qtbase-abi-5-5-1 [ amd64 ] < none -> > ( none )
Considering libqt5core5a:amd64 2904 as a solution to gcin-qt5-immodule:amd64 10000
Considering libqt5core5a:amd64 2904 as a solution to gcin-qt5-immodule:amd64 10000
Done
Some packages could not be installed. This may mean that you have
requested an impossible situation or if you are using the unstable
distribution that some required packages have not yet been created
or been moved out of Incoming.
The following information may help to resolve the situation:

The following packages have unmet dependencies:
gcin-qt5-immodule : Depends: qtbase-abi-5-5-1
E: Unable to correct problems, you have held broken packages.

之前裝 LinuxMint 18 的時候有解過一次這個問題。

解決方法如下

1
2
3
sudo apt download gcin-qt5-immodule
ar x gcin-qt5-immodule_2.8.6+eliu-0_amd64.deb
vim control.tar.gz

tarfile::./control 中的 Depends

Depends: gcin-im-client (>= 2.8.6+eliu-0), libc6 (>= 2.4), libgcc1 (>= 1:3.0), libqt5core5a (>= 5.0.2), libqt5gui5 (>= 5.4.1) | libqt5gui5-gles (>= 5.4.1), qtbase-abi-5-5-1

改為:

Depends: gcin-im-client (>= 2.8.6+eliu-0), libc6 (>= 2.4), libgcc1 (>= 1:3.0), libqt5core5a (>= 5.0.2), libqt5gui5 (>= 5.4.1) | libqt5gui5-gles (>= 5.4.1)

wq 儲存

1
2
ar r gcin-qt5-immodule_2.8.6+eliu-0_amd64.deb control.tar.gz
sudo dpkg -i gcin-qt5-immodule_2.8.6+eliu-0_amd64.deb

重新執行 gcin 即可。

也可以直接下載我包好的:Download

1
2
3
4
~$ md5sum gcin-qt5-immodule_2.8.6+eliu-0_amd64.deb
55ccc5e54dad0665f81b8885c920a00d gcin-qt5-immodule_2.8.6+eliu-0_amd64.deb
~$ sha256sum gcin-qt5-immodule_2.8.6+eliu-0_amd64.deb
87c7edc510f0de68c47a9ababc16ec36c7a765a61343e474e5d6a30f6b1570a3 gcin-qt5-immodule_2.8.6+eliu-0_amd64.deb

gcin 安裝方法

1
~$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 835AB0E3
1
2
3
4
~$ deb http://hyperrate.com/gcin-ubuntu1604 eliu release
~$ deb http://hyperrate.com/gcin-ubuntu1804 eliu release
~$ deb http://hyperrate.com/gcin-ubuntu2004 eliu release
~$ deb http://hyperrate.com/gcin-ubuntu2404 eliu release
1
2
~$ sudo add-apt-repository "deb http://hyperrate.com/gcin-ubuntu2404 eliu release"
~$ sudo apt update
1
~$ im-config -n gcin

用了 3 年多的 LinuxMint 前陣子宣佈 LinuxMint 18.3 是 KDE edition 最後一個版本,然後 .. 就沒有然後了 T_T

索性昨天把系統重新裝成 Kubuntu 18.04 (真心很喜歡 KDE Plasma),不得不說重裝 Unix-like 系統感覺真的超好,重裝 / 保留 /home 的情況,Firefox / Chrome / Opera 全部都不需要重新設定 (設定檔保留於 /home)。

不過裝完 vim 8 之後,遇到一個很大的問題,一直在使用的 vim-copycat(georgefs/vim-copycat)

E319: Sorry, the command isn’t available in this version: python

vim-copycat(georgefs/vim-copycat) 是用於同步系統跟 vim 的剪貼板 (clipboard)

輸入指令:

1
~$ vim --version | grep python

顯示:

1
2
+comments          +libcall           -python            +vreplace
+conceal +linebreak +python3 +wildignore

在 Kubuntu 18.04 使用 apt-get 安裝 Ubuntu repository 中的 vim 已經拿掉了 Python 2 的支援,導致 .vim/bundle/vim-copycat/plugin/copycat.vim 使用 Python 2 出現問題

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
Error detected while processing /home/ming/.vim/bundle/vim-copycat/plugin/copycat.vim:
line 23:
E319: Sorry, the command is not available in this version: python << EOF
line 24:
E492: Not an editor command: import sys
line 25:
E492: Not an editor command: import vim
line 26:
E492: Not an editor command: sys.path.append(vim.eval('expand("<sfile>:p:h:h")'))
line 27:
E492: Not an editor command: import copycat_plugin
line 28:
E492: Not an editor command: EOF

我決定採用解決方式是將 Python 2 的代碼改成 Python 3,下面將一步一步紀錄這個過程。

事前準備:

1
2
sudo pip3 install copycat-clipboard
sudo apt-get install xclip

首先最直觀的根據錯誤紀錄將 .vim/bundle/vim-copycat/plugin/copycat.vim 中的 python 全部取代為 python 3

1
\:1,$ s/python/python3/g

儲存之後,接下來打開 vim 錯誤變成:

1
2
3
4
5
6
7
8
9
10
Error detected while processing /home/ming/.vim/bundle/vim-copycat/plugin/copycat.vim:
line 28:
Traceback (most recent call last):
File "<string>", line 4, in <module>
File "/home/ming/.vim/bundle/vim-copycat/copycat_plugin.py", line 11, in <module>
import copycat
File "/usr/local/lib/python3.6/dist-packages/copycat.py", line 73
print 'no reg {}'.format(name)
^
SyntaxError: invalid syntax

很明顯是 Python 2 的 print 用法,解決的方法很簡單,將所有 copycat.py 中的 print “” 改成 print(“”)

1
:1,$ s/python/python3/g

接下來錯誤變成

1
2
3
4
5
6
7
8
9
10
11
12
13
Error detected while processing function <SNR>36_push_into_clip:                                                                                                                                
line 4:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/home/ming/.vim/bundle/vim-copycat/copycat_plugin.py", line 37, in deco
result = func(*args, **kwargs)
File "/home/ming/.vim/bundle/vim-copycat/copycat_plugin.py", line 53, in copy
copycat.copy(name=name, value=value)
File "/usr/local/lib/python3.6/dist-packages/copycat.py", line 96, in copy
value = smart_str(value)
File "/usr/local/lib/python3.6/dist-packages/copycat.py", line 20, in smart_str
if not isinstance(s, basestring):
NameError: name 'basestring' is not defined

一樣是 copycat.py,將 copycat.py 中的 smart_str 函數改為:

1
2
3
4
5
6
7
8
9
19 def smart_str(s, encoding='utf-8', errors='strict'):
20 #if not isinstance(s, basestring):
21 # s = str(s)
22 #elif isinstance(s, unicode):
23 # return s.encode(encoding, errors)
24 #elif s and encoding != 'utf-8':
25 # return s.decode('utf-8', errors).encode(encoding, errors)
26 #else:
27 return s

錯誤變成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Error detected while processing function <SNR>36_push_into_clip:                                                                                                                                
line 4:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/home/ming/.vim/bundle/vim-copycat/copycat_plugin.py", line 37, in deco
result = func(*args, **kwargs)
File "/home/ming/.vim/bundle/vim-copycat/copycat_plugin.py", line 53, in copy
copycat.copy(name=name, value=value)
File "/usr/local/lib/python3.6/dist-packages/copycat.py", line 100, in copy
clipboard.copy(value)
File "/usr/local/lib/python3.6/dist-packages/clipboard.py", line 31, in copy
pipe.communicate(text)
File "/usr/lib/python3.6/subprocess.py", line 828, in communicate
self._stdin_write(input)
File "/usr/lib/python3.6/subprocess.py", line 781, in _stdin_write
self.stdin.write(input)
TypeError: a bytes-like object is required, not 'str'

將 copycat.py 中的 copy 函數改成:

1
2
3
4
5
6
7
8
def copy(value=None, name=None):
value = value or not sys.stdin.isatty() and sys.stdin.read()

#value = smart_str(value)
with Storage() as storage:
storage.save(value, name=name)
if not name:
clipboard.copy(value.encode('utf-8'))

copy 函數正常後,現在已經可以正常複製,接下來 paste 錯誤訊息:

1
2
3
4
5
6
7
8
9
10
11
12
Error detected while processing function <SNR>36_pop_from_clip:                                                                                                                                 
line 3:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/home/ming/.vim/bundle/vim-copycat/copycat_plugin.py", line 39, in deco
set_to_vim(result_reg, result)
File "/home/ming/.vim/bundle/vim-copycat/copycat_plugin.py", line 25, in set_to_vim
if not isinstance(name, basestring):
NameError: name 'basestring' is not defined
line 4:
E121: Undefined variable: l:result
E15: Invalid expression: l:result

將 copycat.py 中的 paste 函數修改為:

1
2
3
4
5
6
7
8
9
def paste(name=None):
with Storage() as storage:
if not name:
data = clipboard.paste() or storage.get()
else:
data = storage.get(name)
#data = smart_str(data)
clipboard.copy(data)
return data

打開 copycat_plugin.py 將 set_to_vim 函數改成:

1
2
3
4
5
6
7
8
9
10
def set_to_vim(name, value):
#if not isinstance(name, basestring):
# return False

#if isinstance(value, basestring):
value = re.sub(r'([\"\\])', r'\\\1', value.decode('utf-8'))
vim.command('let {}=\"{}\"'.format(name, value))
return True
#return False

完成!

取之於開源,回饋於開源

如果不想要手動改的話,可以參考:

https://github.com/iwdmb/vim-copycat
https://github.com/iwdmb/copycat

我 fork 了 vim-copycat and copycat(copycat-clipboard) 使其支援 Python 3 : )

copycat-clipboard3 pypi repository url:https://pypi.org/project/copycat-clipboard3/

0%