# Define the sender, recipient, subject, and body of the email $From = "sender@example.com" $To = "recipient@example.com" $Subject = "Test Email" $Body = "This is a test email sent via remote SMTP using PowerShell." # Define the SMTP server details $SMTPServer = "smtp.example.com" $SMTPPort = 587 $SMTPUsername = "username" $SMTPPassword = "password" # Create a new email object $Email = New-Object System.Net.Mail.MailMessage $Email.From = $From $Email.To.Add($To) $Email.Subject = $Subject $Email.Body = $Body # Uncomment below to send HTML formatted email #$Email.IsBodyHTML = $true # Create an SMTP client object and send the email $SMTPClient = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort) $SMTPClient.EnableSsl = $true $SMTPClient.Credentials = New-Object System.Net.NetworkCredential($SMTPUsername, $SMTPPassword) $SMTPClient.Send($Email) # Output a message indicating that the email was sent successfully Write-Host "Email sent successfully to $($Email.To.ToString())"
[轉] Powershell Script for Sending Email via Remote SMTP
gitlab runner 【x509: certificate relies on legacy Common Name field, use SANs instead】 And 【x509: certificate signed by unknown authority】
2. Put crt and key to gitlab ssl. Stop gitlab. Start gitlab. Check gitlab have new DNSopenssl genrsa -out ca.key 2048 openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=Acme Root CA" -out ca.crt openssl req -newkey rsa:2048 -nodes -keyout example.com.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=*.example.com" -out example.com.csr openssl x509 -req -extfile <(printf "subjectAltName=DNS:example.com,DNS:www.example.com") -days 365 -in example.com.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out example.com.crt
3. if have 【x509: certificate signed by unknown authority】openssl s_client -connect example.com:443 /dev/null | openssl x509 -noout -text | grep DNS:
gitlab-runner register --tls-ca-file="Use just create crt file"
laravel socialite line How to
Thank
https://medium.com/laravel-news/adding-auth-providers-to-laravel-socialite-ca0335929e42 This have problems https://socialiteproviders.com/Line/1.
composer require socialiteproviders/line
2. create app/Providers/LineProvider.php
https://github.com/SocialiteProviders/Line/blob/master/Provider.php
namespace App\Providers;
use GuzzleHttp\RequestOptions;
use Laravel\Socialite\Two\InvalidStateException;
use \SocialiteProviders\Manager\OAuth2\AbstractProvider;
use \SocialiteProviders\Manager\OAuth2\User;
class LineProvider extends AbstractProvider
{
public const IDENTIFIER = 'LINE';
/**
* The separating character for the requested scopes.
*
* @var string
*/
protected $scopeSeparator = ' ';
/**
* The scopes being requested.
*
* @var array
*/
protected $scopes = [
'openid',
'profile',
'email',
];
/**
* Get the authentication URL for the provider.
*
* @param string $state
* @return string
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase(
'https://access.line.me/oauth2/v2.1/authorize',
$state
);
}
/**
* Get the token URL for the provider.
*
* @return string
*/
protected function getTokenUrl()
{
return 'https://api.line.me/oauth2/v2.1/token';
}
/**
* Get the raw user for the given access token.
*
* @param string $token
* @return array
*/
protected function getUserByToken($token)
{
$response = $this->getHttpClient()->get(
'https://api.line.me/v2/profile',
[
RequestOptions::HEADERS => [
'Authorization' => 'Bearer '.$token,
],
]
);
return json_decode((string) $response->getBody(), true);
}
/**
* Map the raw user array to a Socialite User instance.
*
* @param array $user
* @return \Laravel\Socialite\User
*/
protected function mapUserToObject(array $user)
{
return (new User())->setRaw($user)->map([
'id' => $user['userId'] ?? $user['sub'] ?? null,
'nickname' => null,
'name' => $user['displayName'] ?? $user['name'] ?? null,
'avatar' => $user['pictureUrl'] ?? $user['picture'] ?? null,
'email' => $user['email'] ?? null,
]);
}
// /**
// * Get the POST fields for the token request.
// *
// * @param string $code
// *
// * @return array
// */
// protected function getTokenFields($code)
// {
// return array_merge(parent::getTokenFields($code), [
// 'grant_type' => 'authorization_code',
// ]);
// }
/**
* @return \SocialiteProviders\Manager\OAuth2\User
*/
public function user()
{
if ($this->hasInvalidState()) {
throw new InvalidStateException();
}
$response = $this->getAccessTokenResponse($this->getCode());
if ($jwt = $response['id_token'] ?? null) {
$bodyb64 = explode('.', $jwt)[1];
$user = $this->mapUserToObject(json_decode(base64_decode(strtr($bodyb64, '-_', '+/')), true));
} else {
$user = $this->mapUserToObject($this->getUserByToken(
$token = $this->parseAccessToken($response)
));
}
$this->credentialsResponseBody = $response;
if ($user instanceof User) {
$user->setAccessTokenResponseBody($this->credentialsResponseBody);
}
return $user->setToken($this->parseAccessToken($response))
->setRefreshToken($this->parseRefreshToken($response))
->setExpiresIn($this->parseExpiresIn($response));
}
}
3. edit app/Providers/AppServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
$this->bootLineSocialite();
}
public function bootLineSocialite(): void
{
// // laravel Socialite 和 Socialite Line 有問題,就是 在初始載入不了
// // 看Socialite Line 作者還有在3星期前維謢,所以就不知道怎麼回事
// // https://medium.com/laravel-news/adding-auth-providers-to-laravel-socialite-ca0335929e42
$socialite = $this->app->make('Laravel\Socialite\Contracts\Factory');
$socialite->extend(
'line',
function ($app) use ($socialite) {
$config = config('services.line');
// https://github.com/SocialiteProviders/Line/blob/master/Provider.php
return $socialite->buildProvider(LineProvider::class, $config); // \SocialiteProviders\Line\Provider::class
}
);
}
}
4. routes/web.php
Route::get('/auth/redirect', function () {
return Socialite::driver('line')->redirect();
});
Route::get('/auth/callback', function () {
$user = Socialite::driver('line')->user();
dd($user);
});
[轉]美国教授在雅典论坛上,为中国说点公道话,让香港学生差点喘不过气来【视频11分9秒处】
set alpine data out side of the component
<div x-data="{good_id: '', good_name: ''}" id="good_info" class="w-full" @set_good_data="
good_id = $event.detail.good_id;
good_name = $event.detail.good_name;
">
<div x-text="good_id"></div>
<div x-text="good_name"></div>
</div>
<script>
document.getElementById('good_info').dispatchEvent(new CustomEvent('set_good_data', {
detail: {
good_id: 1234,
good_name: 'Apple',
}
}));
</script>
Laravel Livewire with WinNMP
a. mkdir new directory "t"
b. cd t
c. https://getcomposer.org/download/
c1. php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
c2. php -r "if (hash_file('sha384', 'composer-setup.php') === '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
c3. php composer-setup.php
c4. php -r "unlink('composer-setup.php');"
c5. mv composer.phar to C:\WinNMP\bin\composer
d. rmdir t
3. cd \WinNMP\WWW\livewire
a. delete index.php
b. composer create-project --prefer-dist laravel/laravel .
c. composer require laravel/ui
d. composer install
e. edit .env
e1. DB_DATABASE=livewire
DB_USERNAME=livewire
f. php artisan ui bootstrap --auth
g. php artisan migrate
h. developer tools
composer require doctrine/dbal
composer require --dev barryvdh/laravel-ide-helper
composer require --dev beyondcode/laravel-dump-server
composer require --dev barryvdh/laravel-debugbar
composer require --dev roave/security-advisories:dev-master
php artisan vendor:publish --provider="BeyondCode\DumpServer\DumpServerServiceProvider"
php artisan vendor:publish --provider="Barryvdh\Debugbar\ServiceProvider"
I. php artisan key:generate
4. install node.js for windows
a. https://nodejs.org/zh-tw/download
b. npm install
c. npm run dev
5. nginx setting
a. https://laravel.com/docs/10.x/deployment#nginx
b. WinNMP Projects -> project Setup -> Edit Nginx Virtual Server
6. open http://livewire.test
a. register
b. check db livewire table users have new reocd
7. install livewire
a. use other cmd
b. cd \WinNMP\WWW\livewire
c.
composer dump-autoload
php artisan package:discover
php artisan vendor:publish --tag=livewire:config
composer require livewire/livewire
d. https://laravel-livewire.com/docs/2.x/quickstart
d1. edit resources\views\layouts\app.blade.php
put content
d2.
php artisan make:livewire counter
edit resources\views\layouts\app.blade.php
put
<livewire:counter /> in <main class="py-4"> </main>
d3. edit app\Http\Livewire\Counter.php
public $count = 0;
public function increment()
{
$this->count++;
}
public function render()
{
return view('livewire.counter');
}
d4. edit resources\views\livewire\counter.blade.php
<div style="text-align: center">
<button wire:click="increment">+</button>
<h1>{{ $count }}</h1>
</div>
Codeigniter 4.2 with Eloquent ORM
composer require "illuminate/database"
composer require "illuminate/events"
Config/Events.php
use Illuminate\Database\Capsule\Manager as Capsule;
use Illuminate\Events\Dispatcher;
use Illuminate\Container\Container;
function boostEloquent() {
$capsule = new Capsule;
$capsule->addConnection([
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'database',
'username' => 'root',
'password' => 'password',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
]);
// Set the event dispatcher used by Eloquent models... (optional)
$capsule->setEventDispatcher(new Dispatcher(new Container));
// Make this Capsule instance available globally via static methods... (optional)
$capsule->setAsGlobal();
// Setup the Eloquent ORM... (optional; unless you've used setEventDispatcher())
$capsule->bootEloquent();
}
Events::on('pre_system', function () {
boostEloquent();
if (ENVIRONMENT !== 'testing')
...
}
https://www.modb.pro/db/49966
golang gin windows service
package main
import (
"errors"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
"github.com/kardianos/service"
)
// Process Windows Service // github.com/kardianos/service
var logger service.Logger
type program struct{}
func (p *program) Start(s service.Service) error {
// Start should not block. Do the actual work async.
go p.run()
return nil
}
func (p *program) Stop(s service.Service) error {
// Stop should not block. Return with a few seconds.
return nil
}
func ExecPath() string {
ex, err := os.Executable()
if err != nil {
panic(err)
}
exPath := filepath.Dir(ex)
return exPath
}
func main() {
File, err := os.Create(ExecPath() + "\\gin_service_run.log")
if err != nil {
File = os.Stdout
}
defer File.Close()
log.SetOutput(File)
var s service.Service
svcConfig := service.Config{
Name: "GinAPI",
DisplayName: "Gin API Service",
Description: "Gin API Server Converter Windows Service",
}
prg := &program{}
s, err = service.New(prg, &svcConfig)
if err != nil {
log.Fatalln(err)
}
logger, err = s.Logger(nil)
if err != nil {
log.Fatalln(err)
}
if len(os.Args) > 1 {
switch os.Args[1] {
case "install":
err := s.Install()
if err != nil {
logger.Errorf("Install Service Fail!", err.Error())
return
}
fmt.Printf("Install Service Success!")
case "uninstall":
err := s.Uninstall()
if err != nil {
logger.Errorf("Uninstall Service Fail!", err.Error())
return
}
fmt.Printf("Uninstall Service Success!")
case "start":
err := s.Start()
if err != nil {
logger.Errorf("Start Service Fail!", err.Error())
return
}
fmt.Printf("Start Service Success!")
case "stop":
err := s.Stop()
if err != nil {
logger.Errorf("Stop Service Fail!", err.Error())
return
}
fmt.Printf("Stop Service Success!")
}
return
}
err = s.Run()
if err != nil {
logger.Error(err)
}
}
func (p *program) run() {
//
// Create Error logger
//
errFile, err := os.OpenFile(ExecPath()+"\\errors.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatalf("打開日誌文件失敗:", err)
}
Error = log.New(io.MultiWriter(errFile, os.Stderr), "【Error】", log.Ldate|log.Ltime|log.Lshortfile)
//
// Get .env
//
err = godotenv.Load(ExecPath() + "\\.env")
if err != nil {
msg := "找不到 .env 檔案"
logger.Errorf(msg, err)
Error.Printf(msg, err)
}
web_port := os.Getenv("WEB_PORT")
//
// Start Gin
//
router := gin.Default()
corsConfig := cors.DefaultConfig()
corsConfig.AllowAllOrigins = true
router.Use(cors.New(corsConfig))
router.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "Gin Api Success!"})
})
log.Fatal(http.ListenAndServe(":"+web_port, router))
}
1. run server.exe can run Gin Api Server right now.
2. run server install, server uninstall, server start, server stop.
2.1 【Install and Start】run server install Then server start
2.2 【Stop and uninstall】run server stop Then server uninstall
3. log file use absolute path. ExecPath() can get absolute path.
4. .env must use absolute path. Try to use
svcConfig.EnvVars = map[string]string{
"WEB_PORT": web port
}
see register Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\GinAPI have EnvVars Data.
But err := godotenv.Load() can't get anything, just No Error. So must use absolute path.
5. log have two method.
5.1 File, err := os.Create(ExecPath() + "\\gin_service_run.log")
5.2 【logger.Errorf】 This Error Msg showed at Windows Servce and have Error tag.
OK~ Just try. Hope this blog can help.
Codeigniter 4 ORM relation By hand Part 2
namespace App\Traits;
trait ModelTrait
{
// fetaure Maybe belongsTo
// get result but not effect this, keep this ORM status
public function newSelfObject()
{
return $this->db->query($this->builder->getCompiledSelect(false));
}
// copy self object return ORM Object
public function cso()
{
$class_name = get_class($this);
$class_new_object = (new $class_name);
$class_new_object->builder = clone $this->builder;
return $class_new_object;
}
public function hasOne($class, $relation_primary_key=null, $primary_key=null)
{
return $class->where($relation_primary_key ?? $this->primaryKey, $this->newSelfObject()->getRowArray()[$primary_key ?? $this->primaryKey] ?? '');
}
// whereIn If can't get records, return null. Usually use primaryKey cloud happen
public function hasMany($class, $relation_primary_key=null, $primary_key=null)
{
$temp = array_column($this->newSelfObject()->getResult(), $primary_key ?? $this->primaryKey);
return $class->whereIn($relation_primary_key ?? $this->primaryKey, empty($temp) ? [null] : $temp);
}
}
App\Controllers\OrderListContriller.php
namespace App\Controllers\Order;
class OrderListController extends \App\Controllers\BaseController
{
public function OrderList()
{
// 3.
$order = (new Order)->where("id", $order_id);
$orderData = $order->cso()->findAll(); // Maybe need orderData to do something.
$member = $order->members()->where('createtime >', '2022/11/22')->first(); // ->findAll();
}
}
Codeigniter 4 ORM relation By hand
namespace App\Traits;
trait ModelTrait
{
// fetaure Maybe belongsTo
// get result but not effect this, keep this ORM status
public function newSelfObject()
{
return $this->db->query($this->builder->getCompiledSelect(false));
}
public function hasOne($class, $relation_primary_key=null, $primary_key=null)
{
return $class->where($relation_primary_key ?? $this->primaryKey, $this->newSelfObject()->getRowArray()[$primary_key ?? $this->primaryKey] ?? '');
}
// whereIn If can't get records, return null. Usually use primaryKey cloud happen
public function hasMany($class, $relation_primary_key=null, $primary_key=null)
{
$temp = array_column($this->newSelfObject()->getResult(), $primary_key ?? $this->primaryKey);
return $class->whereIn($relation_primary_key ?? $this->primaryKey, empty($temp) ? [null] : $temp);
}
}
How to use
App\Models\Order.php
class Order extends Model
{
use ModelTrait;
protected $table = 'order';
protected $primaryKey = 'order_id';
public function members()
{
return $this->hasMany(new Member);
// OR
return $this->hasMany(new Member, 'id', 'member_id'); // id always use new Member, member_id use Order
}
App\Controllers\OrderListContriller.php
namespace App\Controllers\Order;
class OrderListController extends \App\Controllers\BaseController
{
public function OrderList()
{
// 1. one order
$members = (new Order)->where('id', $order_id)->members()->first(); // ->findAll();
// 2. All orders
$members = (new Order)->members()->findAll();
// 3.
$order = (new Order)->where("id", $order_id);
$orderData = $order->newSelfObject()->getRow(); // Maybe need orderData to do something.
$member = $order->members()->where('createtime >', '2022/11/22')->first(); // ->findAll();
}
}
You can make belongTo or belongsTo by yourself. More about feature like multiple key, it's more complex
SeckillSolution 秒殺 高并發 交易
https://blog.csdn.net/koastal/article/details/78995885
=====
https://blog.csdn.net/qq_38512763/article/details/118830903
Aggregate array values into ranges
// Output:
// Array (
// [0] => Array ( [0] => 1, [1] => 2, [2] => 3, [3] => 4, [4] => 5, [5] => 6 )
// [1] => Array ( [0] => 10, [1] => 11, [2] => 12, [3] => 13 )
// [2] => Array ( [0] => 20 )
// [3] => Array ( [0] => 24 )
// )
static function GetRanges1( $aNumbers ) {
$aNumbers = array_unique( $aNumbers );
sort( $aNumbers );
$aGroups = array();
for( $i = 0; $i < count( $aNumbers ); $i++ ) {
if( $i > 0 && ( $aNumbers[$i-1] == $aNumbers[$i] - 1 ))
array_push( $aGroups[count($aGroups)-1], $aNumbers[$i] );
else
array_push( $aGroups, array( $aNumbers[$i] ));
}
return $aGroups;
}
// Output: Array ( [0] => 1-6, [1] => 10-13, [2] => 20, [3] => 24 )
static function GetRanges2( $aNumbers ) {
$aNumbers = array_unique( $aNumbers );
sort( $aNumbers );
$aGroups = array();
for( $i = 0; $i < count( $aNumbers ); $i++ ) {
if( $i > 0 && ( $aNumbers[$i-1] == $aNumbers[$i] - 1 ))
array_push( $aGroups[count($aGroups)-1], $aNumbers[$i] );
else
array_push( $aGroups, array( $aNumbers[$i] ));
}
$aRanges = array();
foreach( $aGroups as $aGroup ) {
if( count( $aGroup ) == 1 )
$aRanges[] = $aGroup[0];
else
$aRanges[] = $aGroup[0] . '-' . $aGroup[count($aGroup)-1];
}
return $aRanges;
}
// Output: 1~6,10~13,20,24
static function GetRanges3( $aNumbers, $headChar='' ) {
$aNumbers = array_unique( $aNumbers );
sort( $aNumbers );
$aGroups = array();
for( $i = 0; $i < count( $aNumbers ); $i++ ) {
if( $i > 0 && ( $aNumbers[$i-1] == $aNumbers[$i] - 1 ))
array_push( $aGroups[count($aGroups)-1], $aNumbers[$i] );
else
array_push( $aGroups, array( $aNumbers[$i] ));
}
$aRanges = array();
foreach( $aGroups as $aGroup ) {
if( count( $aGroup ) == 1 )
$aRanges[] = $headChar.$aGroup[0];
else
$aRanges[] = $headChar.$aGroup[0] . '~' . $headChar.$aGroup[count($aGroup)-1];
}
return implode( ',', $aRanges );
}
gorm manytomany many2many use go program
type RoleHasPermissionList struct {
model.Roles
Rolehaspermissions []CUS_Rolehaspermissions `json:"rolehaspermissions" gorm:"foreignkey:role_id; references:id"`
}
type CUS_Rolehaspermissions struct {
PermissionId int `json:"-"`
RoleId int `json:"-"`
PermissinInfo model.Permission `gorm:"foreignkey:id; references:permission_id"`
}
func (CUS_Rolehaspermissions) TableName() string {
return "RoleHasPermissions"
}
type FormaterResult struct {
model.Roles
Permission []model.Permission `json:"permission"`
}
func GetRoleList(c *gin.Context) serializer.Response {
var req GetRoleListRequest
c.ShouldBind(&req)
var total int64
var list []RoleHasPermissionList
page := req.Page
pageSize := req.PageSize
model.DB.
Scopes(util.Paginate(c, page, pageSize)).
Preload("Rolehaspermissions.PermissinInfo").
Find(&list).Offset(-1).Limit(-1).Count(&total)
// 重新整理輸出格式
var formater_result []FormaterResult
for i, v := range list {
formater_result = append(formater_result, FormaterResult{Roles: v.Roles})
for _, v2 := range v.Rolehaspermissions {
formater_result[i].Permission = append(formater_result[i].Permission, v2.PermissinInfo)
}
}
return serializer.Response{
Code: 0,
Message: "成功",
Data: util.Pagination(c, page, pageSize, total, formater_result),
}
}
{
"id": 1,
"name": "company",
"display_name": "公司",
"guard_name": "admin_api",
"created_at": "2020-03-26 14:34:25",
"updated_at": "2020-03-26 14:34:25",
"permission": [
{
"id": 1,
"name": "add company",
"display_name": "add company",
"guard_name": "admin_api",
"created_at": "2020-03-26 14:34:25",
"updated_at": "2020-03-26 14:34:25"
},
{
"id": 2,
"name": "edit company",
"display_name": "edit company",
"guard_name": "admin_api",
"created_at": "2020-03-26 14:34:25",
"updated_at": "2020-03-26 14:34:25"
},
]
},
...
bytes32[] remix solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract Fix {
bytes32[] questionList= [
bytes32(0x52a6eb687cd22e80d3342eac6fcc7f2e19209e8f83eb9b82e81c6f3e6f30743b),
bytes32(0x257f3de9149acf0a49d3f7668956aeb52490202fae9ec2e92c3caa7d5223c6ef)
];
// constructor() {
// // questionList = [
// // bytes32(0x52a6eb687cd22e80d3342eac6fcc7f2e19209e8f83eb9b82e81c6f3e6f30743b),
// // bytes32(0x257f3de9149acf0a49d3f7668956aeb52490202fae9ec2e92c3caa7d5223c6ef)
// // ];
// }
// function addQuestion(bytes32 questionKey)
// public
// returns(bool success)
// {
// questionList.push(questionKey);
// return true;
// }
function getQuestionCount()
public view
returns(uint questionCount)
{
return questionList.length;
}
function getQuestionAtIndex(uint row)
public view
returns(bytes32 questionkey)
{
return questionList[row];
}
}
[轉]After 5 years, I'm out of the serverless compute cult
I have been using serverless computing and storage for nearly five years and I'm finally tired of it. I do feel like it has become a cult. In a cult, brainwashing is done so gradually, people have no idea it is going on. I feel like this has happened across the board with so many developers; many don’t even realize they are clouded. In my case, I took the serverless marketing and hype hook, line, and sinker for the first half of my serverless journey. After working with several companies small and large, I have been continually disappointed as our projects grew. The fact is, serverless technology is amazingly simple to start, but becomes a bear as projects and teams accelerate. A serverless project typically includes a fully serverless stack which can include (using a non-exhaustive list of AWS services):
- API Gateway
- Cognito
- Lambda
- DynamoDB
- DAX
- SQS/SNS/EventBridge
Combining all of these into a serverless project become a huge nightmare for the following reasons.
Testing
All these solutions are proprietary to AWS. Sure, a lambda function is a pretty simple idea; it is simply a function that executes your code. The other services listed above have almost no other easy and testable solutions when integrated together. Serverless Application Model and Localstack have done some amazing work attempting to emulate these services. However, they usually only only cover basic use cases and an engineer ends up spending a chunk of time trying to mock or figure out a way to get their function to test locally. Or, they simply forget it and deploy it. Also, since these functions typically depend on other developer's functions or API Gateway, there tends to be 10 different ways to authorize their function. For example, someone might have an unauthorized API, one may use AWS credentials, another might use Cognito, and yet another uses an API key. All of these factors lead to an engineer having little to no confidence in their ability to test anything locally.
Account Chaos
Since engineers typically don't have a high confidence in their code locally they depend on testing their functions by deploying. This means possibly breaking their own code. As you can imagine, this breaks everyone else deploying and testing any code which relies on the now broken function. While there are a few solutions to this scenario, all are usually quite complex (i.e. using an AWS account per developer) and still cannot be tested locally with much confidence. Chaos engineering has a time and a place. This is not it.
Security
With all the possible permutations of deployments and account structures, security becomes a big problem. Good IAM practices are hard. Many engineers simply put a dynamodb:* for all resources in the account for a lambda function. (BTW this is not good). It becomes hard to manage all of these because developers can usually quite easily deploy and manage their own IAM roles and policies. And since it is hard to test locally, trying to fix serverless IAM issues requires deploying to AWS and testing (or breaking) in the environment.
Bad (Cult-like) Practices
No Fundamental Enforcement
Without help from frameworks, DRY (Don't Repeat Yourself), KISS (Keep It Simple Stupid) and other essential programming paradigms are simply ignored. In a perfect world, a team would reject PR's that do not abide by these basic principles. However, with the huge push for the cloud over the past several years, many junior developers have had the freedom to do what they want in the serverless space because of its ease of use; resulting in developers enmasse adopting something that doesn’t increase the health of the developer ecosystem as a whole. AWS gives you a knife by providing such an easy way to deploy code on the internet. Please don't hurt yourself with it.
Copy and Paste Culture
Most teams end up copying code to the new microservices and proliferating it across many services. I have seen teams with hundreds and even thousands of functions with nearly every function being different. This culture has gotten out of hand and now teams are stuck with these functions. Another symptom of this is not taking the time to provide a proper DNS.
DNS Migration Failures
Developers take the generic API Gateway generated DNS name (abcd1234.amazonaws.com) and litter their code with it. There will come a time when the teams want to put a real DNS in front of it and now you're faced with locating the 200 different spots it was used to change it. And, it's not as easy as a Find/Replace. Searching like this can become a problem when you have a mix of hard-coded strings, parameterized/concatenated strings, and environment variables everywhere that DNS name lies. Oh and telemetry? Yeah that's nowhere to be found.
Microservice Hell
This isn't a post about microservices. However, as teams and developers can decide and add whatever they want into their YAML files for deployment, you end up with hundreds of dependent services and hundreds of repositories. Many have different approaches and/or have different CI/CD workflows. Also, I've found that repository structures begin widely diverging. Any perceived cost savings has now been moved to managing all of these deployments and repositories. Here are a few examples of how developers choose to break up their serverless functions by Git repositories:
- Use a monolith for all their API's.
- Separate each API Gateway or queue processors
- Separate "domain" (i.e.
/customersor/invoices) - Separate by endpoint (I have seen developers break out a repository for
POST:/customerswhile maintaining a separate one forGET:/customers/:idand so on…).
Many times, developers switch between different styles and structures daily. This becomes a nightmare not only for day-to-day development, but also for any developer getting to a quick-understanding of how the code deploys and what dependencies it has or impacts.
API Responses
The serverless cult has been active long enough now that many newer engineers entering the field don't seem to even know about the basics of HTTP responses. Now there are many veteran developers lacking this knowledge. While this is not strictly a serverless problem, I never have experienced this much abuse outside of serverless. I've seen endpoints returning 200, 400, 500 like normal. Yet another set of endpoints return all 2xx responses, with a payload like:
{
"status": "ERROR",
"reason": "malformed input"
}
Then, another set of endpoints implement inconsistent response patterns dependent on some permutation of query parameters. For example:
Query 1:/customers?firstName=John
[{
"accountId": "1234",
"firstName": "John",
"lastName": "Newman"
}]
Query 2:/customers?lastName=Newman
{
"accountId": "1234",
"firstName": "John",
"lastName": "Newman"
}
Inventing New Problems
As mentioned previously, initially deploying these types of services are easy. The reality is there are new problems with these kind of serverless structures that don't typically occur in server-backed services:
- Cold starts - many engineers don't care too much about this. But they suddenly start caring when Function A calls Function B which calls Function C and so on. Without some voodoo warm-up scripting solution, paying for provisioned concurrency, or ignoring it, you may be out of luck.
- In the past five years, prior to our work on our flooring installation marketplace, the teams I have been a part of have always chased the latest features because we had been doing workarounds like FIFO queues, state machines, provisioned concurrency, etc. As teams chase the latest features released by AWS (or your cloud provider of choice), things then become even harder to test and maintain since SAM or Localstack don't match these features for some time.
- Some awful custom eventing solution because… serverless. Engineers think simply putting an API Gateway in front of EventBridge will solve all their eventing problems. What about retries? What about duplicate events? What about replaying events? Schema enforcement? Where does the data land? How do I get the data? These are all questions that have to be answered or documented in a custom fashion. Ok, EventBridge supports a few of these things in some form but it does leave engineers chasing the latest features, waiting for these to become available. However, outside of the serverless cult, these issues can be solved with Kafka, NATS, or other technologies. Use the right tool.
- When it’s not okay to talk about the advantages and disadvantages of serverless with other engineers without fear of reprisal, it might be a cult. Many of these engineers say Lambda is the only way to deploy anymore. There isn't much thought to offline solutions when things need to be run onsite or separated from the cloud. For some companies this can be a fine approach. However, many medium to large organizations have (potentially) offline computing needs outside of the cloud. Lambda cannot provide a sensitive, remote pressure device real-time updates in the event of an internet outage in the middle of Canada during winter.
So, how do I get out of the cult?
In this article, I didn’t plan to address the many options you have to extricate yourself from the grips of mindless serverless abuse. If you're interested, please leave a comment and I will write a follow-up on the different solutions and alternatives to serverless I’ve found as well as some tips to incrementally shift back to a normal life. What I did want to do here was to express the pains I have experienced with serverless technologies over the past couple years now that I have helped architect more traditional VM and container-based tech stacks. I felt compelled to ensure individuals, teams, and organizations know what serverless can really mean for long-term sustainability in an environment.
What does serverless do well?
Deployment and scaling. That's really it for most organizations. For a lot of these organizations, it's hard to find the time, people, and money to figure out how to automatically provision new VM's, get access to a K8S cluster, etc. My challenge to you is to first fix your deployment and scaling problems internally before thinking about serverless compute.
Conclusion
Serverless is one of the hottest new cloud trends. However, I have found it leads to more harm than good in the long run. While I understand some of the problems listed above are not unique to serverless, they are much more prolific; leading engineers to spend most of their time with YAML configuration or troubleshooting function execution rather than crafting business logic. What I find odd is the lack of complaints from the community. If I’m alone in my assessment, I’d love to hear from you in the comments below. I’ve spent a significant amount of time over the last few years working to undo my own serverless mistakes as well as those made by other developers. Maybe I’m the one who has been brainwashed? Time will tell.
jenkins withCredentials sshUserPrivateKey
https://codeleading.com/article/11015806881/ Remeber sshCommand remote, remote infos have identityFile identityFile need be at insdie identity Don't understand identity be keep. Only can be used inside identity
def getRemoteHost(ip, user) {
def remote = [:]
remote.name = ip
remote.host = ip
remote.user = user
remote.identityFile = identity
remote.port = 22
remote.allowAnyHosts = true
return remote
}
pipeline {
agent any
environment {
ssh_ip = 'ooo.xxx.ooo.xxx'
ssh_user = 'ubuntu'
ssh_jenkins_key_uid = 'oooooooo-xxxx-xxxx-xxxx-oooooooooooo'
// SSH_CREDS = credentials('oooooooo-xxxx-xxxx-xxxx-oooooooooooo')
}
stages {
stage('ssh Command'){
steps {
withCredentials([sshUserPrivateKey(credentialsId: "${ssh_jenkins_key_uid}", keyFileVariable: 'identity')]) {
sshCommand remote: getRemoteHost(ssh_ip, ssh_user), command: "whoami" //在远程服务器执行echo命令
}
}
}
}
}
def runCommand(cmd) {
def remote = [:]
remote.name = "${ssh_ip}"
remote.host = "${ssh_ip}"
remote.user = "${ssh_user}"
// remote.identityFile = identity
remote.port = 22
remote.allowAnyHosts = true
withCredentials([sshUserPrivateKey(
credentialsId: "${ssh_jenkins_key_uid}",
keyFileVariable: 'identity')])
{
remote.identityFile = identity
sshCommand remote: remote, command: cmd
}
}
pipeline {
agent any
environment {
ssh_ip = 'ooo.xxx.ooo.xxx'
ssh_user = 'ubuntu'
ssh_jenkins_key_uid = 'oooooooo-xxxx-xxxx-xxxx-oooooooooooo'
// SSH_CREDS = credentials('oooooooo-xxxx-xxxx-xxxx-oooooooooooo')
}
stages {
stage('ssh Command') {
steps {
echo 'whoami start...'
runCommand('whoami')
echo 'whoami success'
}
}
}
}

